Good GPIO Crate for Raspberry Pi with Example Reading Code?

I would like to read GPIO pins on a Raspberry Pi.

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.

Thanks,

-kb

How about rppal GitHub - golemparts/rppal: A Rust library that provides access to the Raspberry Pi's GPIO, I2C, PWM, SPI and UART peripherals.. Looks like it's been updated for Pi 5 already.

Googling for "rapberry pi gpio +rust" turns up a lot of other stuff.

2 Likes

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.

rppal is how I am doing SPI talking to the same hardware (which I got working!), and is what I was playing with yesterday for gpio.

But I'm not finding very good examples so far.

Thanks,

-kb, the Kent who is still looking.

Lots of examples there for doing GPIO output but not input.

Debouncing: I was thinking the kernel would do some magic for me in cases where they hardware doesn't, Oops. Okay, I'll do it myself.

Thanks,

-kb

Are you looking for something in particular?

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.

Very useful!

Thank you very much.

-kb, the Kent who has been chewing on it for the last several hours, making some progress on his code.

1 Like