1use crate::brightness::{self, Brightness};
2use embedded_hal::digital::{OutputPin, PinState, StatefulOutputPin};
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24#[cfg_attr(feature = "defmt", derive(defmt::Format))]
25pub enum PolarityMode {
26 ActiveHigh,
28 ActiveLow,
30}
31
32impl PolarityMode {
33 #[inline]
34 pub(crate) fn physical_on(self) -> PinState {
35 match self {
36 Self::ActiveHigh => PinState::High,
37 Self::ActiveLow => PinState::Low,
38 }
39 }
40
41 #[inline]
42 pub(crate) fn physical_off(self) -> PinState {
43 match self {
44 Self::ActiveHigh => PinState::Low,
45 Self::ActiveLow => PinState::High,
46 }
47 }
48
49 #[inline]
50 pub(crate) fn is_logical_on(self, physical_high: bool) -> bool {
51 match self {
52 Self::ActiveHigh => physical_high,
53 Self::ActiveLow => !physical_high,
54 }
55 }
56
57 #[inline]
58 #[allow(dead_code)] pub(crate) fn map_duty(self, brightness: Brightness) -> Brightness {
60 match self {
61 Self::ActiveHigh => brightness,
62 Self::ActiveLow => brightness::max_value() - brightness,
64 }
65 }
66}
67
68pub struct Led<P> {
92 pin: P,
93 polarity: PolarityMode,
94}
95
96impl<P> Led<P> {
97 #[inline]
102 pub fn from_pin(pin: P, polarity: PolarityMode) -> Self {
103 Self { pin, polarity }
104 }
105}
106
107impl<P: OutputPin> Led<P> {
108 pub fn new(mut pin: P, polarity: PolarityMode) -> Result<Self, P::Error> {
113 pin.set_state(polarity.physical_off())?;
114 Ok(Self { pin, polarity })
115 }
116
117 #[inline]
119 pub fn on(&mut self) -> Result<(), P::Error> {
120 self.pin.set_state(self.polarity.physical_on())
121 }
122
123 #[inline]
125 pub fn off(&mut self) -> Result<(), P::Error> {
126 self.pin.set_state(self.polarity.physical_off())
127 }
128
129 #[inline]
131 pub fn set(&mut self, state: bool) -> Result<(), P::Error> {
132 if state { self.on() } else { self.off() }
133 }
134}
135
136impl<P: StatefulOutputPin> Led<P> {
137 #[inline]
144 pub fn is_on(&mut self) -> Result<bool, P::Error> {
145 self.pin
146 .is_set_high()
147 .map(|h| self.polarity.is_logical_on(h))
148 }
149
150 #[inline]
152 pub fn is_off(&mut self) -> Result<bool, P::Error> {
153 self.is_on().map(|on| !on)
154 }
155
156 #[inline]
162 pub fn toggle(&mut self) -> Result<(), P::Error> {
163 self.pin.toggle()
164 }
165}
166
167impl<P> Led<P> {
168 #[inline]
170 pub fn polarity(&self) -> PolarityMode {
171 self.polarity
172 }
173
174 #[inline]
176 pub fn release(self) -> P {
177 self.pin
178 }
179}
180
181impl<P> core::fmt::Debug for Led<P> {
182 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
183 f.debug_struct("Led")
184 .field("polarity", &self.polarity)
185 .finish_non_exhaustive()
186 }
187}
188
189#[cfg(feature = "defmt")]
190impl<P> defmt::Format for Led<P> {
191 fn format(&self, fmt: defmt::Formatter) {
192 defmt::write!(fmt, "Led {{ polarity: {} }}", self.polarity)
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199 use embedded_hal_mock::eh1::digital::{
200 Mock as PinMock, State as EState, Transaction as PinTrans,
201 };
202
203 #[test]
204 fn new_active_high_sets_off() {
205 let e = [PinTrans::set(EState::Low)];
206 Led::new(PinMock::new(&e), PolarityMode::ActiveHigh)
207 .unwrap()
208 .release()
209 .done();
210 }
211
212 #[test]
213 fn new_active_low_sets_off() {
214 let e = [PinTrans::set(EState::High)];
215 Led::new(PinMock::new(&e), PolarityMode::ActiveLow)
216 .unwrap()
217 .release()
218 .done();
219 }
220
221 #[test]
222 fn from_pin_no_touch() {
223 Led::from_pin(PinMock::new(&[]), PolarityMode::ActiveHigh)
224 .release()
225 .done();
226 }
227
228 #[test]
229 fn active_high_on_off() {
230 let e = [
231 PinTrans::set(EState::Low),
232 PinTrans::set(EState::High),
233 PinTrans::set(EState::Low),
234 ];
235 let mut led = Led::new(PinMock::new(&e), PolarityMode::ActiveHigh).unwrap();
236 led.on().unwrap();
237 led.off().unwrap();
238 led.release().done();
239 }
240
241 #[test]
242 fn active_low_on_off() {
243 let e = [
244 PinTrans::set(EState::High),
245 PinTrans::set(EState::Low),
246 PinTrans::set(EState::High),
247 ];
248 let mut led = Led::new(PinMock::new(&e), PolarityMode::ActiveLow).unwrap();
249 led.on().unwrap();
250 led.off().unwrap();
251 led.release().done();
252 }
253
254 #[test]
255 fn set_state() {
256 let e = [
257 PinTrans::set(EState::Low),
258 PinTrans::set(EState::High),
259 PinTrans::set(EState::Low),
260 ];
261 let mut led = Led::new(PinMock::new(&e), PolarityMode::ActiveHigh).unwrap();
262 led.set(true).unwrap();
263 led.set(false).unwrap();
264 led.release().done();
265 }
266
267 #[test]
268 fn toggle() {
269 let e = [PinTrans::set(EState::Low), PinTrans::toggle()];
270 let mut led = Led::new(PinMock::new(&e), PolarityMode::ActiveHigh).unwrap();
271 led.toggle().unwrap();
272 led.release().done();
273 }
274
275 #[test]
276 fn is_on_active_high() {
277 let e = [PinTrans::set(EState::Low), PinTrans::get_state(EState::Low)];
278 let mut led = Led::new(PinMock::new(&e), PolarityMode::ActiveHigh).unwrap();
279 assert!(!led.is_on().unwrap());
280 led.release().done();
281 }
282
283 #[test]
284 fn is_on_active_low() {
285 let e = [
286 PinTrans::set(EState::High),
287 PinTrans::get_state(EState::High),
288 ];
289 let mut led = Led::new(PinMock::new(&e), PolarityMode::ActiveLow).unwrap();
290 assert!(!led.is_on().unwrap());
291 led.release().done();
292 }
293
294 #[test]
295 fn polarity_accessor() {
296 let led = Led::from_pin(PinMock::new(&[]), PolarityMode::ActiveLow);
297 assert_eq!(led.polarity(), PolarityMode::ActiveLow);
298 led.release().done();
299 }
300}