1use core::marker::PhantomData;
2use embedded_hal::digital::{OutputPin, StatefulOutputPin};
3
4use crate::polarity::{Polarity, PolarityMode};
5
6pub struct Led<P, POL = crate::ActiveHigh> {
31 pin: P,
32 _polarity: PhantomData<POL>,
33}
34
35impl<P, POL> Led<P, POL> {
36 #[inline]
41 pub fn from_pin(pin: P) -> Self {
42 Self {
43 pin,
44 _polarity: PhantomData,
45 }
46 }
47}
48
49impl<P: OutputPin, POL: Polarity> Led<P, POL> {
50 pub fn new(mut pin: P) -> Result<Self, P::Error> {
55 pin.set_state(POL::physical_off())?;
56 Ok(Self {
57 pin,
58 _polarity: PhantomData,
59 })
60 }
61
62 #[inline]
64 pub fn on(&mut self) -> Result<(), P::Error> {
65 self.pin.set_state(POL::physical_on())
66 }
67
68 #[inline]
70 pub fn off(&mut self) -> Result<(), P::Error> {
71 self.pin.set_state(POL::physical_off())
72 }
73
74 #[inline]
76 pub fn set(&mut self, state: bool) -> Result<(), P::Error> {
77 if state { self.on() } else { self.off() }
78 }
79}
80
81impl<P: StatefulOutputPin, POL: Polarity> Led<P, POL> {
82 #[inline]
89 pub fn is_on(&mut self) -> Result<bool, P::Error> {
90 self.pin.is_set_high().map(|h| POL::is_logical_on(h))
91 }
92
93 #[inline]
95 pub fn is_off(&mut self) -> Result<bool, P::Error> {
96 self.is_on().map(|on| !on)
97 }
98
99 #[inline]
105 pub fn toggle(&mut self) -> Result<(), P::Error> {
106 self.pin.toggle()
107 }
108}
109
110impl<P, POL: Polarity> Led<P, POL> {
111 #[inline]
113 pub fn release(self) -> P {
114 self.pin
115 }
116
117 pub fn into_flex(self) -> FlexLed<P> {
119 FlexLed {
120 pin: self.pin,
121 polarity: POL::MODE,
122 }
123 }
124}
125
126impl<P, POL: Polarity> core::fmt::Debug for Led<P, POL> {
127 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
128 f.debug_struct("Led")
129 .field("polarity", &core::any::type_name::<POL>())
130 .finish_non_exhaustive()
131 }
132}
133
134#[cfg(feature = "defmt")]
135impl<P, POL: Polarity> defmt::Format for Led<P, POL> {
136 fn format(&self, fmt: defmt::Formatter) {
137 defmt::write!(
138 fmt,
139 "Led {{ polarity: {} }}",
140 defmt::Display2Format(core::any::type_name::<POL>())
141 )
142 }
143}
144
145pub struct FlexLed<P> {
168 pin: P,
169 polarity: PolarityMode,
170}
171
172impl<P> FlexLed<P> {
173 #[inline]
175 pub fn from_pin(pin: P, polarity: PolarityMode) -> Self {
176 Self { pin, polarity }
177 }
178}
179
180impl<P: OutputPin> FlexLed<P> {
181 pub fn new(mut pin: P, polarity: PolarityMode) -> Result<Self, P::Error> {
183 pin.set_state(polarity.physical_off())?;
184 Ok(Self { pin, polarity })
185 }
186
187 #[inline]
189 pub fn on(&mut self) -> Result<(), P::Error> {
190 self.pin.set_state(self.polarity.physical_on())
191 }
192
193 #[inline]
195 pub fn off(&mut self) -> Result<(), P::Error> {
196 self.pin.set_state(self.polarity.physical_off())
197 }
198
199 #[inline]
201 pub fn set(&mut self, state: bool) -> Result<(), P::Error> {
202 if state { self.on() } else { self.off() }
203 }
204}
205
206impl<P: StatefulOutputPin> FlexLed<P> {
207 #[inline]
209 pub fn is_on(&mut self) -> Result<bool, P::Error> {
210 self.pin
211 .is_set_high()
212 .map(|h| self.polarity.is_logical_on(h))
213 }
214
215 #[inline]
217 pub fn is_off(&mut self) -> Result<bool, P::Error> {
218 self.is_on().map(|on| !on)
219 }
220
221 #[inline]
223 pub fn toggle(&mut self) -> Result<(), P::Error> {
224 self.pin.toggle()
225 }
226}
227
228impl<P> FlexLed<P> {
229 #[inline]
231 pub fn polarity(&self) -> PolarityMode {
232 self.polarity
233 }
234
235 #[inline]
237 pub fn release(self) -> P {
238 self.pin
239 }
240}
241
242impl<P, POL: Polarity> From<Led<P, POL>> for FlexLed<P> {
243 fn from(led: Led<P, POL>) -> Self {
244 led.into_flex()
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251 use embedded_hal_mock::eh1::digital::{
252 Mock as PinMock, State as EState, Transaction as PinTrans,
253 };
254
255 #[test]
256 fn new_active_high_sets_off() {
257 let e = [PinTrans::set(EState::Low)];
258 Led::<_, crate::ActiveHigh>::new(PinMock::new(&e))
259 .unwrap()
260 .release()
261 .done();
262 }
263
264 #[test]
265 fn new_active_low_sets_off() {
266 let e = [PinTrans::set(EState::High)];
267 Led::<_, crate::ActiveLow>::new(PinMock::new(&e))
268 .unwrap()
269 .release()
270 .done();
271 }
272
273 #[test]
274 fn from_pin_no_touch() {
275 Led::<_, crate::ActiveHigh>::from_pin(PinMock::new(&[]))
276 .release()
277 .done();
278 }
279
280 #[test]
281 fn active_high_on_off() {
282 let e = [
283 PinTrans::set(EState::Low),
284 PinTrans::set(EState::High),
285 PinTrans::set(EState::Low),
286 ];
287 let mut led = Led::<_, crate::ActiveHigh>::new(PinMock::new(&e)).unwrap();
288 led.on().unwrap();
289 led.off().unwrap();
290 led.release().done();
291 }
292
293 #[test]
294 fn active_low_on_off() {
295 let e = [
296 PinTrans::set(EState::High),
297 PinTrans::set(EState::Low),
298 PinTrans::set(EState::High),
299 ];
300 let mut led = Led::<_, crate::ActiveLow>::new(PinMock::new(&e)).unwrap();
301 led.on().unwrap();
302 led.off().unwrap();
303 led.release().done();
304 }
305
306 #[test]
307 fn set_state() {
308 let e = [
309 PinTrans::set(EState::Low),
310 PinTrans::set(EState::High),
311 PinTrans::set(EState::Low),
312 ];
313 let mut led = Led::<_, crate::ActiveHigh>::new(PinMock::new(&e)).unwrap();
314 led.set(true).unwrap();
315 led.set(false).unwrap();
316 led.release().done();
317 }
318
319 #[test]
320 fn toggle() {
321 let e = [PinTrans::set(EState::Low), PinTrans::toggle()];
322 let mut led = Led::<_, crate::ActiveHigh>::new(PinMock::new(&e)).unwrap();
323 led.toggle().unwrap();
324 led.release().done();
325 }
326
327 #[test]
328 fn is_on_active_high() {
329 let e = [PinTrans::set(EState::Low), PinTrans::get_state(EState::Low)];
330 let mut led = Led::<_, crate::ActiveHigh>::new(PinMock::new(&e)).unwrap();
331 assert!(!led.is_on().unwrap());
332 led.release().done();
333 }
334
335 #[test]
336 fn is_on_active_low() {
337 let e = [
338 PinTrans::set(EState::High),
339 PinTrans::get_state(EState::High),
340 ];
341 let mut led = Led::<_, crate::ActiveLow>::new(PinMock::new(&e)).unwrap();
342 assert!(!led.is_on().unwrap());
343 led.release().done();
344 }
345
346 #[test]
347 fn flex_new_active_low() {
348 let e = [PinTrans::set(EState::High), PinTrans::set(EState::Low)];
349 let mut led = FlexLed::new(PinMock::new(&e), PolarityMode::ActiveLow).unwrap();
350 led.on().unwrap();
351 led.release().done();
352 }
353
354 #[test]
355 fn flex_from_pin_no_touch() {
356 let led = FlexLed::from_pin(PinMock::new(&[]), PolarityMode::ActiveLow);
357 assert_eq!(led.polarity(), PolarityMode::ActiveLow);
358 led.release().done();
359 }
360
361 #[test]
362 fn flex_toggle() {
363 let e = [PinTrans::set(EState::Low), PinTrans::toggle()];
364 let mut led = FlexLed::new(PinMock::new(&e), PolarityMode::ActiveHigh).unwrap();
365 led.toggle().unwrap();
366 led.release().done();
367 }
368
369 #[test]
370 fn flex_is_on() {
371 let e = [PinTrans::set(EState::Low), PinTrans::get_state(EState::Low)];
372 let mut led = FlexLed::new(PinMock::new(&e), PolarityMode::ActiveHigh).unwrap();
373 assert!(!led.is_on().unwrap());
374 led.release().done();
375 }
376
377 #[test]
378 fn into_flex_preserves_polarity() {
379 let led = Led::<_, crate::ActiveLow>::from_pin(PinMock::new(&[]));
380 let flex: FlexLed<_> = led.into_flex();
381 assert_eq!(flex.polarity(), PolarityMode::ActiveLow);
382 flex.release().done();
383 }
384
385 #[test]
386 fn from_led_to_flex() {
387 let led = Led::<_, crate::ActiveLow>::from_pin(PinMock::new(&[]));
388 let flex = FlexLed::from(led);
389 assert_eq!(flex.polarity(), PolarityMode::ActiveLow);
390 flex.release().done();
391 }
392}