rahul-thakoor / rust_gpiozero

A library inspired by gpiozero written in Rust

Home Page:https://crates.io/crates/rust_gpiozero

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Strange behavior with Button

mariogeiger opened this issue · comments

Hi, Thanks for the nice library, it's faster than python ;-)

I am using a raspberry pi and I tried the following code:

use rust_gpiozero::Debounce;
use rust_gpiozero::*;

use std::{thread, time::Duration};

fn main() -> ! {
    let mut red_button = Button::new(2).debounce(Duration::from_millis(100));

    red_button
        .when_pressed(|x| {
            println!("button pressed {x:?}");
        })
        .unwrap();
    red_button
        .when_released(|x| {
            println!("button released {x:?}");
        })
        .unwrap();
}

What I see when I press the button twice:

$ cargo run
   Compiling rationalizer v0.1.0 (/home/happy/rationalizer)
    Finished dev [unoptimized + debuginfo] target(s) in 3.75s
     Running `target/debug/rationalizer`
button released Low
button released Low
button released Low
button released Low

What I expected:

$ cargo run
   Compiling rationalizer v0.1.0 (/home/happy/rationalizer)
    Finished dev [unoptimized + debuginfo] target(s) in 3.75s
     Running `target/debug/rationalizer`
button pressed High
button released Low
button pressed High
button released Low

I can confirm the issue (though rather than four button released Low I get two when pressing twice). I tried switching the order: when when_pressed comes after when_released, the output is two button pressed High. I also tried without debounce and that didn't change anything, though perhaps @thepacketgeek has thoughts nonetheless having worked on some button functionality?

tl; dr: Any previous async interrupt pin triggers are cleared when set_async_interrupt is called, hence setting when_released clears the trigger when_pressed in your example. Closing since this behavior originates from a dependency.

Details: Both when_pressed and when_released call action_on, which calls self.pin.set_async_interrupt(trigger, action) of the dependency rppal. The function set_async_interrupt has the following documentation:

    /// Configures an asynchronous interrupt trigger, which executes the callback on a
    /// separate thread when the interrupt is triggered.
    ///
    /// The callback closure or function pointer is called with a single [`Level`] argument.
    ///
    /// Any previously configured (a)synchronous interrupt triggers for this pin are cleared
    /// when `set_async_interrupt` is called, or when `InputPin` goes out of scope.
    ///
    /// [`clear_async_interrupt`]: #method.clear_async_interrupt
    /// [`Level`]: enum.Level.html
    pub fn set_async_interrupt<C>(&mut self, trigger: Trigger, callback: C) -> Result<()>
    where
        C: FnMut(Level) + Send + 'static,
    {