Merge pull request #2505 from CBJamo/timeout_at

Add timeout_at convenience function and example.
This commit is contained in:
Dario Nieuwenhuis 2024-01-31 21:31:40 +00:00 committed by GitHub
commit e05f6505ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 95 additions and 2 deletions

View file

@ -32,7 +32,7 @@ pub use delay::{block_for, Delay};
pub use duration::Duration; pub use duration::Duration;
pub use embassy_time_driver::TICK_HZ; pub use embassy_time_driver::TICK_HZ;
pub use instant::Instant; pub use instant::Instant;
pub use timer::{with_timeout, Ticker, TimeoutError, Timer}; pub use timer::{with_deadline, with_timeout, Ticker, TimeoutError, Timer};
const fn gcd(a: u64, b: u64) -> u64 { const fn gcd(a: u64, b: u64) -> u64 {
if b == 0 { if b == 0 {

View file

@ -8,7 +8,7 @@ use futures_util::{pin_mut, Stream};
use crate::{Duration, Instant}; use crate::{Duration, Instant};
/// Error returned by [`with_timeout`] on timeout. /// Error returned by [`with_timeout`] and [`with_deadline`] on timeout.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TimeoutError; pub struct TimeoutError;
@ -26,6 +26,19 @@ pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Out
} }
} }
/// Runs a given future with a deadline time.
///
/// If the future completes before the deadline, its output is returned. Otherwise, on timeout,
/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.
pub async fn with_deadline<F: Future>(at: Instant, fut: F) -> Result<F::Output, TimeoutError> {
let timeout_fut = Timer::at(at);
pin_mut!(fut);
match select(fut, timeout_fut).await {
Either::Left((r, _)) => Ok(r),
Either::Right(_) => Err(TimeoutError),
}
}
/// A future that completes at a specified [Instant](struct.Instant.html). /// A future that completes at a specified [Instant](struct.Instant.html).
#[must_use = "futures do nothing unless you `.await` or poll them"] #[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct Timer { pub struct Timer {

View file

@ -0,0 +1,80 @@
//! This example shows the ease of debouncing a button with async rust.
//! Hook up a button or switch between pin 9 and ground.
#![no_std]
#![no_main]
use defmt::info;
use embassy_executor::Spawner;
use embassy_rp::gpio::{Input, Level, Pull};
use embassy_time::{with_deadline, Duration, Instant, Timer};
use {defmt_rtt as _, panic_probe as _};
pub struct Debouncer<'a> {
input: Input<'a>,
debounce: Duration,
}
impl<'a> Debouncer<'a> {
pub fn new(input: Input<'a>, debounce: Duration) -> Self {
Self { input, debounce }
}
pub async fn debounce(&mut self) -> Level {
loop {
let l1 = self.input.get_level();
self.input.wait_for_any_edge().await;
Timer::after(self.debounce).await;
let l2 = self.input.get_level();
if l1 != l2 {
break l2;
}
}
}
}
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_rp::init(Default::default());
let mut btn = Debouncer::new(Input::new(p.PIN_9, Pull::Up), Duration::from_millis(20));
info!("Debounce Demo");
loop {
// button pressed
btn.debounce().await;
let start = Instant::now();
info!("Button Press");
match with_deadline(start + Duration::from_secs(1), btn.debounce()).await {
// Button Released < 1s
Ok(_) => {
info!("Button pressed for: {}ms", start.elapsed().as_millis());
continue;
}
// button held for > 1s
Err(_) => {
info!("Button Held");
}
}
match with_deadline(start + Duration::from_secs(5), btn.debounce()).await {
// Button released <5s
Ok(_) => {
info!("Button pressed for: {}ms", start.elapsed().as_millis());
continue;
}
// button held for > >5s
Err(_) => {
info!("Button Long Held");
}
}
// wait for button release before handling another press
btn.debounce().await;
info!("Button pressed for: {}ms", start.elapsed().as_millis());
}
}