Search code examples
rustlibgpiodgpiod

How can I read the state of a GPIO output in Rust?


I'm trying to implement a stateless process that doesn't have to track the state of the GPIO it's controlling: rather, I was hoping to read directly from the hardware if pins are set low or high.

For inputs, this doesn't appear to be a problem, but for outputs, it apparently is.

I'm using the gpiod crate for rust, which is based on libgpiod. The same behaviour described in this issue: Get value of a gpio output using libgpiod in Linux is what I'm facing.

Is there an "as-is" option, much like with more recent cli gpioget tools to accomplish this?

This code is meant to toggle a given line on a given Chip ('chip'):

    let opts /* : Options<Output, [u32; 1], ... */ = Options::output([line]);
    let outputs : Lines<Output> = chip.request_lines(opts)?;
    let mut values: [bool; 1] = outputs.get_values([false])?;

    // Negate the values read from the 'outputs'
    values.iter_mut().for_each(|x| *x = !*x);

    // Switch the polarity of the lines on the outputs
    outputs.set_values(values)?;

    std::thread::sleep(std::time::Duration::from_micros(hold_time_us));

    // Return the values read from the 'outputs' to their original state.
    values.iter_mut().for_each(|x| *x = !*x);

    // Return the outputs to their original state
    outputs.set_values(values)?;

but as soon as outputs goes out of scope, I don't know how to recover the state.


Solution

  • Looks to me like the gpiod crate does not support as-is, only input or output.

    You could use libgpiod's Rust bindings, i.e the libgpiod crate, or my gpiocdev crate, both of those do support requesting a line as-is.

    Requesting a line as an output always overrides the value, which you don't want, but you need to set the line as an output to set its value, so you would need to request it as-is, then reconfigure it as an output with the toggled value:

        let offset = 22;
        // request the line as-is
        let req = gpiocdev::Request::builder()
            .on_chip("/dev/gpiochip0")
            .with_line(offset)
            .request()?;
    
        // read the value
        let value = req.value(offset)?;
        println!("{offset}: {value} -> {}", value.not());
    
        // reconfigure the line as output with inverted value
        let mut config = req.config();
        req.reconfigure(config.as_output(value.not()))?;
    

    Though, as @0andriy mentions, whether that works the way you want depends on the kernel GPIO driver for your hardware - the typical behaviour is to reset the line to its default state (often an input) when released. It works for me on a Raspberry Pi (compiled as toggle_line_value):

    $ toggle_line_value
    22: inactive -> active
    $ toggle_line_value
    22: active -> inactive
    $ toggle_line_value
    22: inactive -> active
    

    as the Raspberry Pi GPIO driver leaves the line in its current state when released. YMMV.