embedded_simple_ui/
led.rs1use embedded_hal::digital::{PinState, StatefulOutputPin};
2use embedded_time::duration::Milliseconds;
3use embedded_time::rate::Rate;
4use embedded_time::{Clock, Instant};
5
6use self::effects::LedEffect;
7
8pub mod effects {
9 use embedded_time::{duration::Milliseconds, rate::Hertz, Clock, Instant, TimeInt};
10
11 #[derive(Copy, Clone, Debug)]
13 pub enum EffectType<T: TimeInt = u32> {
14 Pulse(Milliseconds<T>),
16 Blink(Hertz<T>),
18 }
19
20 #[derive(Copy, Clone, Debug)]
25 pub struct LedEffect<C: Clock> {
26 current_cycle_started_at: Option<Instant<C>>,
27 started_at: Option<Instant<C>>,
28 duration: Option<Milliseconds<C::T>>,
29 fx_type: EffectType<C::T>,
30 }
31
32 impl<C: Clock> LedEffect<C> {
33 pub fn new(fx_type: EffectType<C::T>) -> Self {
35 Self {
36 current_cycle_started_at: None,
37 fx_type,
38 duration: None,
39 started_at: None
40 }
41 }
42
43 pub fn has_started(&self) -> bool {
45 self.started_at.is_some()
46 }
47
48 pub fn started_at(&self) -> Option<Instant<C>> {
50 self.started_at
51 }
52
53 pub fn set_started_at(&mut self, now: Instant<C>) {
57 self.started_at = Some(now);
58 self.current_cycle_started_at = self.started_at;
59 }
60
61 pub fn get_type(&self) -> &EffectType<C::T> {
63 &self.fx_type
64 }
65
66 pub fn get_duration(&self) -> Option<Milliseconds<C::T>> {
68 self.duration
69 }
70
71 pub fn set_duration(&mut self, dur: Milliseconds<C::T>) {
73 self.duration = Some(dur)
74 }
75
76 pub fn time_elapsed(&self, now: Instant<C>) -> Option<Milliseconds<C::T>> {
78 if let Some(started_at) = &self.started_at {
79 return now
80 .checked_duration_since(started_at)
81 .map(|d| Milliseconds::<C::T>::try_from(d).unwrap());
82 }
83 None
84 }
85
86 pub fn current_cycle_duration(&self, now: Instant<C>) -> Option<Milliseconds<C::T>> {
88 if let Some(started_at) = &self.current_cycle_started_at {
89 return now
90 .checked_duration_since(started_at)
91 .map(|d| Milliseconds::<C::T>::try_from(d).unwrap());
92 }
93 None
94 }
95
96 pub fn start_new_cycle(&mut self, now: Instant<C>) {
98 self.current_cycle_started_at = Some(now);
99 }
100 }
101
102 #[inline]
103 pub fn pulse<C: Clock>(duration_ms: u16) -> EffectType<C::T> {
104 let v = C::T::from(duration_ms.into());
105 EffectType::Pulse::<C::T>(Milliseconds::<C::T>::new(v))
106 }
107
108 #[inline]
109 pub fn blink<C: Clock>(rate_hz: u8) -> EffectType<C::T> {
110 let v = C::T::from(rate_hz.into());
111 EffectType::Blink::<C::T>(Hertz::<C::T>::new(v))
112 }
113}
114
115pub trait Led<C: Clock> {
124 fn is_on(&mut self) -> bool;
126
127 fn turn_on(&mut self);
131
132 fn turn_off(&mut self);
136
137 fn toggle(&mut self);
139
140 fn set_effect(&mut self, effect: effects::LedEffect<C>);
148
149 fn set_effect_duration(&mut self, dur: Milliseconds<C::T>);
155
156 fn get_effect(&self) -> Option<&LedEffect<C>>;
160
161 fn clear_effect(&mut self);
166
167 fn poll(&mut self, now: Instant<C>);
173}
174
175pub struct PinLed<P: StatefulOutputPin, C: Clock> {
176 pin: P,
177 effect: Option<effects::LedEffect<C>>,
178 is_on: bool,
179}
180
181impl<P: StatefulOutputPin, C: Clock> PinLed<P, C> {
182 pub fn new(pin: P) -> Self {
183 Self { pin, effect: None, is_on: false }
184 }
185}
186
187impl<P: StatefulOutputPin, C: Clock> Led<C> for PinLed<P, C> {
188 fn is_on(&mut self) -> bool {
189 self.is_on
190 }
191
192 fn turn_on(&mut self) {
193 self.is_on = true;
194 }
195
196 fn turn_off(&mut self) {
197 self.is_on = false;
198 }
199
200 fn toggle(&mut self) {
201 self.is_on = !self.is_on;
202 }
203
204 fn set_effect(&mut self, effect: effects::LedEffect<C>) {
205 self.effect = Some(effect);
206 }
207
208 fn set_effect_duration(&mut self, dur: Milliseconds<<C as Clock>::T>) {
209 if let Some(fx) = &mut self.effect {
210 fx.set_duration(dur)
211 }
212 }
213
214 fn clear_effect(&mut self) {
215 self.effect = None;
216 self.turn_off();
217 }
218
219 fn poll(&mut self, now: Instant<C>) {
220 if let Some(fx) = &mut self.effect {
221 let elapsed = fx.time_elapsed(now);
224
225 if let Some(fx_dur) = fx.get_duration() {
227 if let Some(elapsed) = elapsed {
228 if elapsed > fx_dur {
229 self.clear_effect();
231 self.pin.set_low().unwrap();
232 return;
233 }
234 }
235 }
236
237 let mut clear_effect = false;
238
239 match fx.get_type() {
240 effects::EffectType::Pulse(dur) => {
241 if let Some(current_dur) = fx.current_cycle_duration(now) {
242 if current_dur > *dur {
243 clear_effect = true;
245 self.pin.set_low().unwrap();
246 } else if ! fx.has_started() {
247 self.pin.set_high().unwrap();
248 }
249 }
250 }
251 effects::EffectType::Blink(rate) => {
252 if let Some(current_dur) = fx.current_cycle_duration(now) {
253 if current_dur > rate.to_duration::<Milliseconds<C::T>>().unwrap() {
254 if self.pin.is_set_low().unwrap() {
256 self.pin.set_high().unwrap();
257 } else {
258 self.pin.set_low().unwrap();
259 }
260 fx.start_new_cycle(now);
261 }
262 }
263 }
264 }
265
266 if clear_effect {
267 self.clear_effect();
268 return;
269 }
270
271 if ! fx.has_started() {
273 fx.set_started_at(now);
274 }
275 } else {
276 let state = self.is_on;
278 self.pin
279 .set_state(match state {
280 false => PinState::High,
281 true => PinState::Low,
282 })
283 .unwrap()
284 }
285 }
286
287 fn get_effect(&self) -> Option<&LedEffect<C>> {
288 self.effect.as_ref()
289 }
290}