1#![no_std]
2#![doc = include_str!("../README.md")]
3
4use embedded_hal::digital::InputPin;
5
6pub type Millis = u64;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum ActiveLevel {
12 High,
14 Low,
16}
17
18impl ActiveLevel {
19 #[inline]
20 const fn state_from_high(self, is_high: bool) -> ButtonState {
21 match (self, is_high) {
22 (Self::High, true) | (Self::Low, false) => ButtonState::Pressed,
23 (Self::High, false) | (Self::Low, true) => ButtonState::Released,
24 }
25 }
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum ButtonState {
31 Pressed,
33 Released,
35}
36
37impl ButtonState {
38 #[inline]
40 pub const fn is_pressed(self) -> bool {
41 matches!(self, Self::Pressed)
42 }
43
44 #[inline]
46 pub const fn is_released(self) -> bool {
47 matches!(self, Self::Released)
48 }
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub struct ButtonConfig {
54 pub debounce_ms: Millis,
57 pub double_click_ms: Millis,
60 pub hold_ms: Millis,
62 pub multi_click_count: u8,
66 pub multi_click_timeout_ms: Millis,
68 pub active_level: ActiveLevel,
70}
71
72impl ButtonConfig {
73 pub const DEFAULT_DEBOUNCE_MS: Millis = 20;
75 pub const DEFAULT_DOUBLE_CLICK_MS: Millis = 300;
77 pub const DEFAULT_HOLD_MS: Millis = 800;
79 pub const DEFAULT_MULTI_CLICK_COUNT: u8 = 0;
81 pub const DEFAULT_MULTI_CLICK_TIMEOUT_MS: Millis = 3_000;
83
84 #[inline]
87 pub const fn active_low() -> Self {
88 Self {
89 debounce_ms: Self::DEFAULT_DEBOUNCE_MS,
90 double_click_ms: Self::DEFAULT_DOUBLE_CLICK_MS,
91 hold_ms: Self::DEFAULT_HOLD_MS,
92 multi_click_count: Self::DEFAULT_MULTI_CLICK_COUNT,
93 multi_click_timeout_ms: Self::DEFAULT_MULTI_CLICK_TIMEOUT_MS,
94 active_level: ActiveLevel::Low,
95 }
96 }
97
98 #[inline]
100 pub const fn active_high() -> Self {
101 Self {
102 active_level: ActiveLevel::High,
103 ..Self::active_low()
104 }
105 }
106
107 #[inline]
109 pub const fn with_debounce_ms(mut self, debounce_ms: Millis) -> Self {
110 self.debounce_ms = debounce_ms;
111 self
112 }
113
114 #[inline]
116 pub const fn with_double_click_ms(mut self, double_click_ms: Millis) -> Self {
117 self.double_click_ms = double_click_ms;
118 self
119 }
120
121 #[inline]
123 pub const fn with_hold_ms(mut self, hold_ms: Millis) -> Self {
124 self.hold_ms = hold_ms;
125 self
126 }
127
128 #[inline]
133 pub const fn with_multi_click_count(mut self, multi_click_count: u8) -> Self {
134 self.multi_click_count = multi_click_count;
135 self
136 }
137
138 #[inline]
144 pub const fn with_multi_click_timeout_ms(mut self, multi_click_timeout_ms: Millis) -> Self {
145 self.multi_click_timeout_ms = multi_click_timeout_ms;
146 self
147 }
148
149 #[inline]
151 pub const fn with_multi_click(
152 mut self,
153 multi_click_count: u8,
154 multi_click_timeout_ms: Millis,
155 ) -> Self {
156 self.multi_click_count = multi_click_count;
157 self.multi_click_timeout_ms = multi_click_timeout_ms;
158 self
159 }
160}
161
162impl Default for ButtonConfig {
163 #[inline]
164 fn default() -> Self {
165 Self::active_low()
166 }
167}
168
169#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
171pub struct ButtonEvents {
172 pub pressed: bool,
174 pub released: bool,
176 pub click: bool,
178 pub double_click: bool,
180 pub multi_click: bool,
183 pub hold: bool,
185}
186
187impl ButtonEvents {
188 #[inline]
190 pub const fn any(self) -> bool {
191 self.pressed
192 || self.released
193 || self.click
194 || self.double_click
195 || self.multi_click
196 || self.hold
197 }
198}
199
200#[derive(Debug, Clone, Copy, PartialEq, Eq)]
202pub struct ButtonUpdate {
203 pub state: ButtonState,
205 pub events: ButtonEvents,
207 pub held_ms: Option<Millis>,
209 pub released_after_ms: Option<Millis>,
211 pub multi_click_count: u8,
217}
218
219impl ButtonUpdate {
220 #[inline]
221 const fn idle(state: ButtonState, held_ms: Option<Millis>, multi_click_count: u8) -> Self {
222 Self {
223 state,
224 events: ButtonEvents {
225 pressed: false,
226 released: false,
227 click: false,
228 double_click: false,
229 multi_click: false,
230 hold: false,
231 },
232 held_ms,
233 released_after_ms: None,
234 multi_click_count,
235 }
236 }
237}
238
239pub struct Button<PIN> {
245 pin: PIN,
246 config: ButtonConfig,
247 raw_state: ButtonState,
248 raw_changed_at: Millis,
249 stable_state: ButtonState,
250 pressed_at: Option<Millis>,
251 pending_click_at: Option<Millis>,
252 multi_click_seen: u8,
253 multi_click_last_at: Option<Millis>,
254 hold_reported: bool,
255}
256
257impl<PIN> Button<PIN> {
258 #[inline]
260 pub const fn new(pin: PIN) -> Self {
261 Self::with_config(pin, ButtonConfig::active_low())
262 }
263
264 #[inline]
266 pub const fn with_config(pin: PIN, config: ButtonConfig) -> Self {
267 Self {
268 pin,
269 config,
270 raw_state: ButtonState::Released,
271 raw_changed_at: 0,
272 stable_state: ButtonState::Released,
273 pressed_at: None,
274 pending_click_at: None,
275 multi_click_seen: 0,
276 multi_click_last_at: None,
277 hold_reported: false,
278 }
279 }
280
281 #[inline]
283 pub const fn config(&self) -> ButtonConfig {
284 self.config
285 }
286
287 #[inline]
289 pub fn set_config(&mut self, config: ButtonConfig) {
290 self.config = config;
291
292 if config.multi_click_count < 2 {
293 self.reset_multi_click();
294 }
295 }
296
297 #[inline]
299 pub const fn debounce_ms(&self) -> Millis {
300 self.config.debounce_ms
301 }
302
303 #[inline]
305 pub fn set_debounce_ms(&mut self, debounce_ms: Millis) {
306 self.config.debounce_ms = debounce_ms;
307 }
308
309 #[inline]
311 pub const fn double_click_ms(&self) -> Millis {
312 self.config.double_click_ms
313 }
314
315 #[inline]
317 pub fn set_double_click_ms(&mut self, double_click_ms: Millis) {
318 self.config.double_click_ms = double_click_ms;
319 }
320
321 #[inline]
323 pub const fn hold_ms(&self) -> Millis {
324 self.config.hold_ms
325 }
326
327 #[inline]
329 pub fn set_hold_ms(&mut self, hold_ms: Millis) {
330 self.config.hold_ms = hold_ms;
331 }
332
333 #[inline]
337 pub const fn multi_click_count(&self) -> u8 {
338 self.config.multi_click_count
339 }
340
341 #[inline]
345 pub fn set_multi_click_count(&mut self, multi_click_count: u8) {
346 self.config.multi_click_count = multi_click_count;
347
348 if multi_click_count < 2 {
349 self.reset_multi_click();
350 }
351 }
352
353 #[inline]
356 pub const fn multi_click_timeout_ms(&self) -> Millis {
357 self.config.multi_click_timeout_ms
358 }
359
360 #[inline]
362 pub fn set_multi_click_timeout_ms(&mut self, multi_click_timeout_ms: Millis) {
363 self.config.multi_click_timeout_ms = multi_click_timeout_ms;
364 }
365
366 #[inline]
368 pub fn reset_multi_click(&mut self) {
369 self.multi_click_seen = 0;
370 self.multi_click_last_at = None;
371 }
372
373 #[inline]
374 fn visible_multi_click_count(&self) -> u8 {
375 if self.config.multi_click_count < 2 {
376 0
377 } else {
378 self.multi_click_seen
379 }
380 }
381
382 #[inline]
384 pub const fn state(&self) -> ButtonState {
385 self.stable_state
386 }
387
388 #[inline]
391 pub const fn is_pressed(&self) -> bool {
392 self.stable_state.is_pressed()
393 }
394
395 #[inline]
398 pub const fn is_released(&self) -> bool {
399 self.stable_state.is_released()
400 }
401
402 #[inline]
404 pub fn held_ms(&self, now_ms: Millis) -> Option<Millis> {
405 self.pressed_at
406 .map(|pressed_at| elapsed(now_ms, pressed_at))
407 }
408
409 #[inline]
411 pub fn pin_mut(&mut self) -> &mut PIN {
412 &mut self.pin
413 }
414
415 #[inline]
417 pub fn into_inner(self) -> PIN {
418 self.pin
419 }
420}
421
422impl<PIN> Button<PIN>
423where
424 PIN: InputPin,
425{
426 pub fn update(&mut self, now_ms: Millis) -> Result<ButtonUpdate, PIN::Error> {
459 let raw_state = self.read_raw_state()?;
460 self.reset_expired_multi_click(now_ms);
461
462 if raw_state != self.raw_state {
463 self.raw_state = raw_state;
464 self.raw_changed_at = now_ms;
465 }
466
467 let mut update = ButtonUpdate::idle(
468 self.stable_state,
469 self.held_ms(now_ms),
470 self.visible_multi_click_count(),
471 );
472
473 if self.stable_state != self.raw_state
474 && elapsed(now_ms, self.raw_changed_at) >= self.config.debounce_ms
475 {
476 self.accept_stable_state(now_ms, &mut update);
477 }
478
479 if self.stable_state == ButtonState::Pressed {
480 let held_ms = self.held_ms(now_ms).unwrap_or(0);
481 update.held_ms = Some(held_ms);
482
483 if !self.hold_reported && held_ms >= self.config.hold_ms {
484 update.events.hold = true;
485 self.hold_reported = true;
486 }
487 }
488
489 self.report_pending_click(now_ms, &mut update);
490 update.state = self.stable_state;
491 Ok(update)
492 }
493
494 #[inline]
495 fn read_raw_state(&mut self) -> Result<ButtonState, PIN::Error> {
496 self.pin
497 .is_high()
498 .map(|is_high| self.config.active_level.state_from_high(is_high))
499 }
500
501 fn accept_stable_state(&mut self, now_ms: Millis, update: &mut ButtonUpdate) {
502 self.stable_state = self.raw_state;
503
504 match self.stable_state {
505 ButtonState::Pressed => {
506 self.pressed_at = Some(now_ms);
507 self.hold_reported = false;
508 update.events.pressed = true;
509 update.held_ms = Some(0);
510 }
511 ButtonState::Released => {
512 let held_ms = self
513 .pressed_at
514 .map(|pressed_at| elapsed(now_ms, pressed_at));
515
516 self.pressed_at = None;
517 self.hold_reported = false;
518 update.events.released = true;
519 update.held_ms = None;
520 update.released_after_ms = held_ms;
521
522 if held_ms.is_some_and(|held_ms| held_ms < self.config.hold_ms) {
523 self.register_short_release(now_ms, update);
524 } else {
525 self.reset_multi_click();
526 }
527 }
528 }
529 }
530
531 fn register_short_release(&mut self, now_ms: Millis, update: &mut ButtonUpdate) {
532 let multi_click_fired = self.register_multi_click(now_ms, update);
533
534 if self.pending_click_at.is_some_and(|pending_click_at| {
535 elapsed(now_ms, pending_click_at) <= self.config.double_click_ms
536 }) {
537 update.events.double_click = true;
538 self.pending_click_at = None;
539 } else if multi_click_fired {
540 self.pending_click_at = None;
541 } else {
542 self.report_pending_click(now_ms, update);
543 self.pending_click_at = Some(now_ms);
544 }
545 }
546
547 fn report_pending_click(&mut self, now_ms: Millis, update: &mut ButtonUpdate) {
548 if self.has_active_multi_click_sequence(now_ms) {
549 return;
550 }
551
552 if self.pending_click_at.is_some_and(|pending_click_at| {
553 elapsed(now_ms, pending_click_at) > self.config.double_click_ms
554 }) {
555 update.events.click = true;
556 self.pending_click_at = None;
557 }
558 }
559
560 fn register_multi_click(&mut self, now_ms: Millis, update: &mut ButtonUpdate) -> bool {
561 if self.config.multi_click_count < 2 {
562 update.multi_click_count = 0;
563 return false;
564 }
565
566 self.reset_expired_multi_click(now_ms);
567
568 self.multi_click_seen = self.multi_click_seen.saturating_add(1);
569 self.multi_click_last_at = Some(now_ms);
570 update.multi_click_count = self.multi_click_seen;
571
572 if self.multi_click_seen >= self.config.multi_click_count {
573 update.events.multi_click = true;
574 update.multi_click_count = self.config.multi_click_count;
575 self.reset_multi_click();
576 return true;
577 }
578
579 false
580 }
581
582 fn reset_expired_multi_click(&mut self, now_ms: Millis) {
583 if self
584 .multi_click_last_at
585 .is_some_and(|last_at| elapsed(now_ms, last_at) > self.config.multi_click_timeout_ms)
586 {
587 self.reset_multi_click();
588 }
589 }
590
591 fn has_active_multi_click_sequence(&self, now_ms: Millis) -> bool {
592 self.config.multi_click_count >= 2
593 && self.multi_click_seen > 0
594 && self.multi_click_last_at.is_some_and(|last_at| {
595 elapsed(now_ms, last_at) <= self.config.multi_click_timeout_ms
596 })
597 }
598}
599
600#[inline]
601const fn elapsed(now_ms: Millis, earlier_ms: Millis) -> Millis {
602 now_ms.saturating_sub(earlier_ms)
603}
604
605#[cfg(test)]
606extern crate std;
607
608#[cfg(test)]
609mod tests {
610 use std::io::ErrorKind;
611
612 use embedded_hal_mock::eh1::{
613 digital::{Mock as PinMock, State as PinState, Transaction as PinTransaction},
614 MockError,
615 };
616
617 use super::*;
618
619 fn config() -> ButtonConfig {
620 ButtonConfig::active_low()
621 .with_debounce_ms(10)
622 .with_double_click_ms(200)
623 .with_hold_ms(1000)
624 }
625
626 #[test]
627 fn filters_bounce_and_reports_press_release() {
628 let expectations = [
629 PinTransaction::get(PinState::High),
630 PinTransaction::get(PinState::Low),
631 PinTransaction::get(PinState::High),
632 PinTransaction::get(PinState::Low),
633 PinTransaction::get(PinState::Low),
634 PinTransaction::get(PinState::Low),
635 PinTransaction::get(PinState::High),
636 PinTransaction::get(PinState::High),
637 PinTransaction::get(PinState::High),
638 PinTransaction::get(PinState::High),
639 ];
640 let pin = PinMock::new(&expectations);
641 let mut button = Button::with_config(pin, config());
642
643 assert_eq!(button.update(0).unwrap().state, ButtonState::Released);
644 assert!(!button.update(1).unwrap().events.any());
645 assert!(!button.update(5).unwrap().events.any());
646 assert!(!button.update(7).unwrap().events.any());
647 assert!(!button.update(16).unwrap().events.any());
648
649 let update = button.update(17).unwrap();
650 assert_eq!(update.state, ButtonState::Pressed);
651 assert!(update.events.pressed);
652 assert_eq!(update.held_ms, Some(0));
653
654 assert!(!button.update(30).unwrap().events.any());
655 assert!(!button.update(35).unwrap().events.any());
656
657 let update = button.update(40).unwrap();
658 assert_eq!(update.state, ButtonState::Released);
659 assert!(update.events.released);
660 assert!(!update.events.click);
661 assert!(!update.events.double_click);
662 assert_eq!(update.released_after_ms, Some(23));
663
664 let update = button.update(241).unwrap();
665 assert_eq!(update.state, ButtonState::Released);
666 assert!(update.events.click);
667 assert!(!update.events.double_click);
668 assert_eq!(update.released_after_ms, None);
669
670 button.into_inner().done();
671 }
672
673 #[test]
674 fn detects_double_click() {
675 let expectations = [
676 PinTransaction::get(PinState::Low),
677 PinTransaction::get(PinState::Low),
678 PinTransaction::get(PinState::High),
679 PinTransaction::get(PinState::High),
680 PinTransaction::get(PinState::Low),
681 PinTransaction::get(PinState::Low),
682 PinTransaction::get(PinState::High),
683 PinTransaction::get(PinState::High),
684 ];
685 let pin = PinMock::new(&expectations);
686 let mut button = Button::with_config(pin, config().with_debounce_ms(5).with_hold_ms(1000));
687
688 assert!(!button.update(0).unwrap().events.any());
689 assert!(button.update(5).unwrap().events.pressed);
690
691 assert!(!button.update(20).unwrap().events.any());
692 let update = button.update(25).unwrap();
693 assert!(update.events.released);
694 assert!(!update.events.click);
695 assert!(!update.events.double_click);
696
697 assert!(!button.update(80).unwrap().events.any());
698 assert!(button.update(85).unwrap().events.pressed);
699
700 assert!(!button.update(100).unwrap().events.any());
701 let update = button.update(105).unwrap();
702 assert!(update.events.released);
703 assert!(!update.events.click);
704 assert!(update.events.double_click);
705
706 button.into_inner().done();
707 }
708
709 #[test]
710 fn detects_configured_multi_click_count() {
711 let expectations = [
712 PinTransaction::get(PinState::Low),
713 PinTransaction::get(PinState::Low),
714 PinTransaction::get(PinState::High),
715 PinTransaction::get(PinState::High),
716 PinTransaction::get(PinState::Low),
717 PinTransaction::get(PinState::Low),
718 PinTransaction::get(PinState::High),
719 PinTransaction::get(PinState::High),
720 PinTransaction::get(PinState::Low),
721 PinTransaction::get(PinState::Low),
722 PinTransaction::get(PinState::High),
723 PinTransaction::get(PinState::High),
724 PinTransaction::get(PinState::Low),
725 PinTransaction::get(PinState::Low),
726 PinTransaction::get(PinState::High),
727 PinTransaction::get(PinState::High),
728 PinTransaction::get(PinState::Low),
729 PinTransaction::get(PinState::Low),
730 PinTransaction::get(PinState::High),
731 PinTransaction::get(PinState::High),
732 ];
733 let pin = PinMock::new(&expectations);
734 let mut button = Button::with_config(
735 pin,
736 config()
737 .with_debounce_ms(5)
738 .with_hold_ms(1000)
739 .with_multi_click(5, 3_000),
740 );
741
742 assert_eq!(button.multi_click_count(), 5);
743 assert_eq!(button.multi_click_timeout_ms(), 3_000);
744
745 assert!(!button.update(0).unwrap().events.any());
746 assert!(button.update(5).unwrap().events.pressed);
747 assert!(!button.update(20).unwrap().events.any());
748 let update = button.update(25).unwrap();
749 assert!(update.events.released);
750 assert_eq!(update.multi_click_count, 1);
751 assert!(!update.events.multi_click);
752
753 assert!(!button.update(80).unwrap().events.any());
754 assert!(button.update(85).unwrap().events.pressed);
755 assert!(!button.update(100).unwrap().events.any());
756 let update = button.update(105).unwrap();
757 assert!(update.events.released);
758 assert_eq!(update.multi_click_count, 2);
759 assert!(!update.events.multi_click);
760
761 assert!(!button.update(160).unwrap().events.any());
762 assert!(button.update(165).unwrap().events.pressed);
763 assert!(!button.update(180).unwrap().events.any());
764 let update = button.update(185).unwrap();
765 assert!(update.events.released);
766 assert_eq!(update.multi_click_count, 3);
767 assert!(!update.events.multi_click);
768
769 assert!(!button.update(240).unwrap().events.any());
770 assert!(button.update(245).unwrap().events.pressed);
771 assert!(!button.update(260).unwrap().events.any());
772 let update = button.update(265).unwrap();
773 assert!(update.events.released);
774 assert_eq!(update.multi_click_count, 4);
775 assert!(!update.events.multi_click);
776
777 assert!(!button.update(320).unwrap().events.any());
778 assert!(button.update(325).unwrap().events.pressed);
779 assert!(!button.update(340).unwrap().events.any());
780 let update = button.update(345).unwrap();
781 assert!(update.events.released);
782 assert_eq!(update.multi_click_count, 5);
783 assert!(update.events.multi_click);
784
785 button.into_inner().done();
786 }
787
788 #[test]
789 fn resets_multi_click_count_after_timeout() {
790 let expectations = [
791 PinTransaction::get(PinState::Low),
792 PinTransaction::get(PinState::Low),
793 PinTransaction::get(PinState::High),
794 PinTransaction::get(PinState::High),
795 PinTransaction::get(PinState::Low),
796 PinTransaction::get(PinState::Low),
797 PinTransaction::get(PinState::High),
798 PinTransaction::get(PinState::High),
799 PinTransaction::get(PinState::High),
800 PinTransaction::get(PinState::Low),
801 PinTransaction::get(PinState::Low),
802 PinTransaction::get(PinState::High),
803 PinTransaction::get(PinState::High),
804 ];
805 let pin = PinMock::new(&expectations);
806 let mut button = Button::with_config(
807 pin,
808 config()
809 .with_debounce_ms(5)
810 .with_hold_ms(1000)
811 .with_multi_click(3, 3_000),
812 );
813
814 assert!(!button.update(0).unwrap().events.any());
815 assert!(button.update(5).unwrap().events.pressed);
816 assert!(!button.update(20).unwrap().events.any());
817 let update = button.update(25).unwrap();
818 assert_eq!(update.multi_click_count, 1);
819 assert!(!update.events.multi_click);
820
821 assert!(!button.update(80).unwrap().events.any());
822 assert!(button.update(85).unwrap().events.pressed);
823 assert!(!button.update(100).unwrap().events.any());
824 let update = button.update(105).unwrap();
825 assert_eq!(update.multi_click_count, 2);
826 assert!(!update.events.multi_click);
827
828 let update = button.update(3106).unwrap();
829 assert!(!update.events.any());
830 assert_eq!(update.multi_click_count, 0);
831
832 assert!(!button.update(3200).unwrap().events.any());
833 assert!(button.update(3205).unwrap().events.pressed);
834 assert!(!button.update(3220).unwrap().events.any());
835 let update = button.update(3225).unwrap();
836 assert_eq!(update.multi_click_count, 1);
837 assert!(!update.events.multi_click);
838
839 button.into_inner().done();
840 }
841
842 #[test]
843 fn configurable_double_click_can_use_multi_click_timeout() {
844 let expectations = [
845 PinTransaction::get(PinState::Low),
846 PinTransaction::get(PinState::Low),
847 PinTransaction::get(PinState::High),
848 PinTransaction::get(PinState::High),
849 PinTransaction::get(PinState::Low),
850 PinTransaction::get(PinState::Low),
851 PinTransaction::get(PinState::High),
852 PinTransaction::get(PinState::High),
853 ];
854 let pin = PinMock::new(&expectations);
855 let mut button = Button::with_config(
856 pin,
857 config()
858 .with_debounce_ms(5)
859 .with_double_click_ms(200)
860 .with_hold_ms(1000)
861 .with_multi_click(2, 3_000),
862 );
863
864 assert!(!button.update(0).unwrap().events.any());
865 assert!(button.update(5).unwrap().events.pressed);
866 assert!(!button.update(20).unwrap().events.any());
867 let update = button.update(25).unwrap();
868 assert!(update.events.released);
869 assert_eq!(update.multi_click_count, 1);
870 assert!(!update.events.click);
871 assert!(!update.events.double_click);
872 assert!(!update.events.multi_click);
873
874 assert!(!button.update(1000).unwrap().events.any());
875 assert!(button.update(1005).unwrap().events.pressed);
876 assert!(!button.update(1020).unwrap().events.any());
877 let update = button.update(1025).unwrap();
878 assert!(update.events.released);
879 assert!(!update.events.click);
880 assert!(!update.events.double_click);
881 assert_eq!(update.multi_click_count, 2);
882 assert!(update.events.multi_click);
883
884 button.into_inner().done();
885 }
886
887 #[test]
888 fn reports_hold_once_and_tracks_hold_time() {
889 let expectations = [
890 PinTransaction::get(PinState::Low),
891 PinTransaction::get(PinState::Low),
892 PinTransaction::get(PinState::Low),
893 PinTransaction::get(PinState::Low),
894 PinTransaction::get(PinState::Low),
895 PinTransaction::get(PinState::High),
896 PinTransaction::get(PinState::High),
897 ];
898 let pin = PinMock::new(&expectations);
899 let mut button = Button::with_config(pin, config().with_debounce_ms(5).with_hold_ms(50));
900
901 assert!(!button.update(0).unwrap().events.any());
902 assert!(button.update(5).unwrap().events.pressed);
903
904 let update = button.update(54).unwrap();
905 assert_eq!(update.held_ms, Some(49));
906 assert!(!update.events.hold);
907
908 let update = button.update(55).unwrap();
909 assert_eq!(update.held_ms, Some(50));
910 assert!(update.events.hold);
911
912 let update = button.update(80).unwrap();
913 assert_eq!(update.held_ms, Some(75));
914 assert!(!update.events.hold);
915
916 assert!(!button.update(100).unwrap().events.any());
917 let update = button.update(105).unwrap();
918 assert!(update.events.released);
919 assert!(!update.events.click);
920 assert_eq!(update.released_after_ms, Some(100));
921
922 button.into_inner().done();
923 }
924
925 #[test]
926 fn supports_active_high_and_runtime_debounce_setting() {
927 let expectations = [
928 PinTransaction::get(PinState::Low),
929 PinTransaction::get(PinState::High),
930 PinTransaction::get(PinState::High),
931 ];
932 let pin = PinMock::new(&expectations);
933 let mut button = Button::with_config(pin, ButtonConfig::active_high());
934
935 button.set_debounce_ms(3);
936 assert_eq!(button.debounce_ms(), 3);
937
938 assert_eq!(button.update(0).unwrap().state, ButtonState::Released);
939 assert!(!button.update(1).unwrap().events.any());
940
941 let update = button.update(4).unwrap();
942 assert_eq!(update.state, ButtonState::Pressed);
943 assert!(update.events.pressed);
944
945 button.into_inner().done();
946 }
947
948 #[test]
949 fn propagates_pin_errors() {
950 let expectations =
951 [PinTransaction::get(PinState::High).with_error(MockError::Io(ErrorKind::Other))];
952 let pin = PinMock::new(&expectations);
953 let mut button = Button::new(pin);
954
955 assert!(button.update(0).is_err());
956
957 button.into_inner().done();
958 }
959}