My first puzzle is what crate to use. Any suggestions?
My second puzzle is how to use it. Pointers to example code for reading GPIO puns?
I made pretty good progress with the crate gpiod but then discovered that it doesn't seem to implement input debouncing, which is frustrating because I think the gpiochip/Linux kernel does. Also, my initializations seem to be incomplete, I can read all the lines I want after a run some related Python GPIO code, but not after a fresh boot. So good example code/documentation would be nice.
I second rppal, we use it in production to access GPIO pins and SPI and UART devices, and works very well. I find the documentation (previous links) to be good. There's many examples in the repo.
Regarding debouncing I'm not sure, we do it in hardware.
I have not looked into rappal but I suspect you will have to do your own debouncing. After all such input filtering is rather dependent on the switch or whatever other input you have. It's not hard to do.
Here's an example using two inputs configured with internal pull-up resistors and connected to two (imaginary) buttons:
use std::error::Error;
use std::io;
use rppal::gpio::{Gpio, Trigger}; // 0.15.0
pub const BTN1: u8 = 13;
pub const BTN2: u8 = 16;
fn main() -> Result<(), Box<dyn Error>> {
let mut btns = [
Gpio::new()?.get(BTN1)?.into_input_pullup(),
Gpio::new()?.get(BTN2)?.into_input_pullup(),
];
for (i, btn) in btns.iter_mut().enumerate() {
btn.set_reset_on_drop(false);
btn.set_async_interrupt(Trigger::Both, move |lvl| eprintln!("btn {i} is now {lvl}"))?;
}
eprintln!("Play with the buttons, and hit Enter when done to terminate.");
let mut s = String::new();
io::stdin().read_line(&mut s)?;
Ok(())
}
I use into_input_pullup to configure the GPIO pin as input with internal pull-up resistor.
I call set_reset_on_drop(false) to avoid that the pull-up resistor is disconnected when the program terminates (up to you and your hardware design).
With set_async_interrupt I pass in a closure that will be executed every time the input transitions low->high and every time it transitions high->low (Trigger::Both)
Finally, I wait for user input to not terminate the program.
If you run the program, and start touching the buttons, the program will print something like:
Play with the buttons, and hit Enter when done to terminate.
btn 0 is now Low
btn 0 is now High
btn 1 is now Low
btn 0 is now Low
btn 0 is now High
btn 1 is now High
Instead of the asynchronous interrupt, you can use the synchronous version by first registering with set_interrupt what trigger you want (rising, falling, both), then calling poll_interrupt to block until the event happens.
Or you can just check whether the pin is_high or is_low.