use embedded_hal::digital::{PinState, StatefulOutputPin};
use embedded_time::duration::Milliseconds;
use embedded_time::rate::Rate;
use embedded_time::{Clock, Instant};
use self::effects::LedEffect;
pub mod effects {
use embedded_time::{duration::Milliseconds, rate::Hertz, Clock, Instant, TimeInt};
#[derive(Copy, Clone, Debug)]
pub enum EffectType<T: TimeInt = u32> {
Pulse(Milliseconds<T>),
Blink(Hertz<T>),
}
#[derive(Copy, Clone, Debug)]
pub struct LedEffect<C: Clock> {
current_cycle_started_at: Option<Instant<C>>,
started_at: Option<Instant<C>>,
duration: Option<Milliseconds<C::T>>,
fx_type: EffectType<C::T>,
}
impl<C: Clock> LedEffect<C> {
pub fn new(fx_type: EffectType<C::T>) -> Self {
Self {
current_cycle_started_at: None,
fx_type,
duration: None,
started_at: None
}
}
pub fn has_started(&self) -> bool {
self.started_at.is_some()
}
pub fn started_at(&self) -> Option<Instant<C>> {
self.started_at
}
pub fn set_started_at(&mut self, now: Instant<C>) {
self.started_at = Some(now);
self.current_cycle_started_at = self.started_at;
}
pub fn get_type(&self) -> &EffectType<C::T> {
&self.fx_type
}
pub fn get_duration(&self) -> Option<Milliseconds<C::T>> {
self.duration
}
pub fn set_duration(&mut self, dur: Milliseconds<C::T>) {
self.duration = Some(dur)
}
pub fn time_elapsed(&self, now: Instant<C>) -> Option<Milliseconds<C::T>> {
if let Some(started_at) = &self.started_at {
return now
.checked_duration_since(started_at)
.map(|d| Milliseconds::<C::T>::try_from(d).unwrap());
}
None
}
pub fn current_cycle_duration(&self, now: Instant<C>) -> Option<Milliseconds<C::T>> {
if let Some(started_at) = &self.current_cycle_started_at {
return now
.checked_duration_since(started_at)
.map(|d| Milliseconds::<C::T>::try_from(d).unwrap());
}
None
}
pub fn start_new_cycle(&mut self, now: Instant<C>) {
self.current_cycle_started_at = Some(now);
}
}
#[inline]
pub fn pulse<C: Clock>(duration_ms: u16) -> EffectType<C::T> {
let v = C::T::from(duration_ms.into());
EffectType::Pulse::<C::T>(Milliseconds::<C::T>::new(v))
}
#[inline]
pub fn blink<C: Clock>(rate_hz: u8) -> EffectType<C::T> {
let v = C::T::from(rate_hz.into());
EffectType::Blink::<C::T>(Hertz::<C::T>::new(v))
}
}
pub trait Led<C: Clock> {
fn is_on(&mut self) -> bool;
fn turn_on(&mut self);
fn turn_off(&mut self);
fn toggle(&mut self);
fn set_effect(&mut self, effect: effects::LedEffect<C>);
fn set_effect_duration(&mut self, dur: Milliseconds<C::T>);
fn get_effect(&self) -> Option<&LedEffect<C>>;
fn clear_effect(&mut self);
fn poll(&mut self, now: Instant<C>);
}
pub struct PinLed<P: StatefulOutputPin, C: Clock> {
pin: P,
effect: Option<effects::LedEffect<C>>,
is_on: bool,
}
impl<P: StatefulOutputPin, C: Clock> PinLed<P, C> {
pub fn new(pin: P) -> Self {
Self { pin, effect: None, is_on: false }
}
}
impl<P: StatefulOutputPin, C: Clock> Led<C> for PinLed<P, C> {
fn is_on(&mut self) -> bool {
self.is_on
}
fn turn_on(&mut self) {
self.is_on = true;
}
fn turn_off(&mut self) {
self.is_on = false;
}
fn toggle(&mut self) {
self.is_on = !self.is_on;
}
fn set_effect(&mut self, effect: effects::LedEffect<C>) {
self.effect = Some(effect);
}
fn set_effect_duration(&mut self, dur: Milliseconds<<C as Clock>::T>) {
if let Some(fx) = &mut self.effect {
fx.set_duration(dur)
}
}
fn clear_effect(&mut self) {
self.effect = None;
self.turn_off();
}
fn poll(&mut self, now: Instant<C>) {
if let Some(fx) = &mut self.effect {
let elapsed = fx.time_elapsed(now);
if let Some(fx_dur) = fx.get_duration() {
if let Some(elapsed) = elapsed {
if elapsed > fx_dur {
self.clear_effect();
self.pin.set_low().unwrap();
return;
}
}
}
let mut clear_effect = false;
match fx.get_type() {
effects::EffectType::Pulse(dur) => {
if let Some(current_dur) = fx.current_cycle_duration(now) {
if current_dur > *dur {
clear_effect = true;
self.pin.set_low().unwrap();
} else if ! fx.has_started() {
self.pin.set_high().unwrap();
}
}
}
effects::EffectType::Blink(rate) => {
if let Some(current_dur) = fx.current_cycle_duration(now) {
if current_dur > rate.to_duration::<Milliseconds<C::T>>().unwrap() {
if self.pin.is_set_low().unwrap() {
self.pin.set_high().unwrap();
} else {
self.pin.set_low().unwrap();
}
fx.start_new_cycle(now);
}
}
}
}
if clear_effect {
self.clear_effect();
return;
}
if ! fx.has_started() {
fx.set_started_at(now);
}
} else {
let state = self.is_on;
self.pin
.set_state(match state {
false => PinState::High,
true => PinState::Low,
})
.unwrap()
}
}
fn get_effect(&self) -> Option<&LedEffect<C>> {
self.effect.as_ref()
}
}