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