Git Product home page Git Product logo

rust-sysfs-gpio's Introduction

sysfs_gpio

Build Status Version Minimum Supported Rust Version License

The sysfs_gpio crate provides access to the Linux sysfs GPIO interface (https://www.kernel.org/doc/Documentation/gpio/sysfs.txt). It seeks to provide an API that is safe, convenient, and efficient and supports exporting, unexporting, reading, writing and waiting for interrupts on pins.

Many devices such as the Raspberry Pi or Beaglebone Black provide userspace access to a number of GPIO peripherals. The standard kernel API for providing access to these GPIOs is via sysfs.

You might want to also check out the gpio-utils Project for a convenient way to associate names with pins and export them as part of system boot. That project uses this library.

Install/Use

To use sysfs_gpio, first add this to your Cargo.toml:

[dependencies]
sysfs_gpio = "0.6"

Then, add this to your crate root:

use sysfs_gpio;

Example/API

Blinking an LED:

use sysfs_gpio::{Direction, Pin};
use std::thread::sleep;
use std::time::Duration;

fn main() {
    let my_led = Pin::new(127); // number depends on chip, etc.
    my_led.with_exported(|| {
        my_led.set_direction(Direction::Out).unwrap();
        loop {
            my_led.set_value(0).unwrap();
            sleep(Duration::from_millis(200));
            my_led.set_value(1).unwrap();
            sleep(Duration::from_millis(200));
        }
    }).unwrap();
}

More Examples:

Features

The following features are planned for the library:

  • Support for exporting a GPIO
  • Support for unexporting a GPIO
  • Support for setting the direction of a GPIO (in/out)
  • Support for reading the value of a GPIO input
  • Support for writing the value of a GPIO output
  • Support for configuring whether a pin is active low/high
  • Support for configuring interrupts on GPIO
  • Support for polling on GPIO with configured interrupt
  • Support for asynchronous polling using mio or tokio (requires enabling the mio-evented or async-tokio crate features, respectively)

Minimum Supported Rust Version (MSRV)

This crate is guaranteed to compile on stable Rust 1.65.0 and up. It might compile with older versions but that may change in any new patch release.

Cross Compiling

Most likely, the machine you are running on is not your development machine (although it could be). In those cases, you will need to cross-compile. The rust-cross guide provides excellent, detailed instructions for cross-compiling.

Running the Example

Cross-compiling can be done by specifying an appropriate target. You can then move that to your device by whatever means and run it.

$ cargo build --target=arm-unknown-linux-gnueabihf --example blinky
$ scp target/arm-unknown-linux-gnueabihf/debug/examples/blinky ...

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Code of Conduct

Contribution to this crate is organized under the terms of the Rust Code of Conduct, the maintainer of this crate, the Embedded Linux Team, promises to intervene to uphold that code of conduct.

rust-sysfs-gpio's People

Contributors

bors[bot] avatar eldruin avatar emosenkis avatar jonas-schievink avatar julianwinterdmde avatar jwilm avatar leseulartichaut avatar m-ou-se avatar marjakm avatar martinsp avatar mlakewood avatar nastevens avatar oll3 avatar pheki avatar posborne avatar rahul-thakoor avatar razican avatar rumatoest avatar ryankurte avatar scootermon avatar tpmanley avatar zaynetro avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rust-sysfs-gpio's Issues

Pin can be exported twice

I'm using the following code:

fn open_door(gpio_pin: u64) -> GpioResult<()> {
    let pin = Pin::new(gpio_pin);
    pin.with_exported(|| {
        try!(pin.set_direction(Direction::Out));
        try!(pin.set_value(1));
        sleep(Duration::from_secs(OPEN_SECONDS));
        try!(pin.set_value(0));
        Ok(())
    })
}

If I run this program twice in parallel, the pin is exported without errors, but the second unexport fails:

Opening door on GPIO 17 for 5 seconds...
Opening failed: Io(Error { repr: Os { code: 2, message: "No such file or directory" } })

Can this be handled somehow?

Allow compilation to wasm32-wasi target

Hello!

I have a use case where I would like to use a subset of the functionality of rust-sysfs-gpio. I am compiling a was--compatible module that can access gpio pins via a webassembly runtime.

async/.await support - "How to" discussion

Hi,
I want to implement a 'native' async/.await API for this crate. Since I am not the most experienced rust programmer I'm not sure about how to go about it. Obviously one could just upgrade to the new tokio version and go with it. Because I am contemplating if I should switch my project form tokio to async-std I'm wondering if an executor agnostic solution (if possible) should be preferred for this crate.
I'll do some more digging on this topic but I wanted to leave this issue in case somebody else with more knowledge than me comes by and contributes to this discussion and wants to collaborate.
Best regards!

Incompatible with tokio crate

Hi,

i want to use the PinValueStreams together with the newer version of tokio but if i try to use the API, I of course get the following compiler error:
"expected struct tokio_core::reactor::Handle, found struct tokio::reactor::Handle"

For now I will just use the AsyncPinPoller and construct the PinValueStream myself. Would it be possible to add support for the "tokio" crate and remain backwards compatibility ?

Non blocking interrupt

Similar libraries for GPIO support in other languages (Python RPi.GPIO, WiringPi, etc), provide a background thread for interrupt detection, and callback attaching. For example in python:

RPi.GPIO runs a second thread for callback functions. This means that callback functions can be run at the same time as your main program, in immediate response to an edge. For example:

def my_callback(channel):
    print('This is a edge event callback function!')
    print('Edge detected on channel %s'%channel)
    print('This is run in a different thread to your main program')

GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback)  # add rising edge detection

I know this is difficult to do in Rust with all the safety, ownership and the like, but that will be great to have.

Need a sleep/timeout after exporting pin?

If I run blinky example with pi user then I receive error:

pi@raspberrypi /sys/class/gpio $ id
uid=1000(pi) gid=1000(pi) groups=1000(pi),4(adm),20(dialout),24(cdrom),27(sudo),29(audio),44(video),46(plugdev),60(games),100(users),105(netdev),110(i2c),999(input),1002(spi),1003(gpio)
pi@raspberrypi ~ $ ./blinky 8 800 400
We have a blinking problem: Permission denied (os error 13)

If I run it with root user, everything is working correctly.

By trial and error I have found that after exporting a pin at least a 80ms sleep is required before exported pin can be manipulated by users in group gpio.

If I change blink_my_led function in blinky.rs to

my_led.with_exported(|| {
        sleep_ms(80);
        ...

then everything runs successfully with a pi user.

I have found that when pin is exported for short period of time the owner of the exported pin is root:root. Later the owner is changed to root:gpio. During the period when owner is root:root users in gpio group can't operate the pin.

pi@raspberrypi /sys/class/gpio $ echo 8 > unexport
pi@raspberrypi /sys/class/gpio $ echo 8 > export; ls -l gpio8/*
-rw-r--r-- 1 root root 4096 Aug 10 10:56 gpio8/active_low
lrwxrwxrwx 1 root root    0 Aug 10 10:56 gpio8/device -> ../../../20200000.gpio
-rw-r--r-- 1 root root 4096 Aug 10 10:56 gpio8/direction
-rw-r--r-- 1 root root 4096 Aug 10 10:56 gpio8/edge
lrwxrwxrwx 1 root root    0 Aug 10 10:56 gpio8/subsystem -> ../../../../../class/gpio
-rw-r--r-- 1 root root 4096 Aug 10 10:56 gpio8/uevent
-rw-r--r-- 1 root root 4096 Aug 10 10:56 gpio8/value
pi@raspberrypi /sys/class/gpio $ ls -l gpio8/*
-rwxrwx--- 1 root gpio 4096 Aug 10 10:56 gpio8/active_low
lrwxrwxrwx 1 root gpio    0 Aug 10 10:56 gpio8/device -> ../../../20200000.gpio
-rwxrwx--- 1 root gpio 4096 Aug 10 10:56 gpio8/direction
-rwxrwx--- 1 root gpio 4096 Aug 10 10:56 gpio8/edge
lrwxrwxrwx 1 root gpio    0 Aug 10 10:56 gpio8/subsystem -> ../../../../../class/gpio
-rwxrwx--- 1 root gpio 4096 Aug 10 10:56 gpio8/uevent
-rwxrwx--- 1 root gpio 4096 Aug 10 10:56 gpio8/value

I'm not sure if this issue is specific to my Raspbery Pi / Raspbian version or this is how gpio sysfs works on Raspberry Pi. Maybe it should be documented somewhere that a timeout is required after exporting pin?

Raspberry Pi model 1 B+

pi@raspberrypi ~ $ uname -a
Linux raspberrypi 3.18.11+ #781 PREEMPT Tue Apr 21 18:02:18 BST 2015 armv6l GNU/Linux
pi@raspberrypi ~ $ cat /etc/debian_version
7.8

Upstream deprecation

The upstream sysfs interface is deprecated:

THIS ABI IS DEPRECATED, THE ABI DOCUMENTATION HAS BEEN MOVED TO
Documentation/ABI/obsolete/sysfs-gpio AND NEW USERSPACE CONSUMERS
ARE SUPPOSED TO USE THE CHARACTER DEVICE ABI. THIS OLD SYSFS ABI WILL
NOT BE DEVELOPED (NO NEW FEATURES), IT WILL JUST BE MAINTAINED.

As the bare IOCTLs may not be suitable to interact with these devices, it may be better to use libgpiod:

libgpiod - C library and tools for interacting with the linux GPIO character device (gpiod stands for GPIO device)
Since linux 4.8 the GPIO sysfs interface is deprecated. User space should use
the character device instead. This library encapsulates the ioctl calls and
data structures behind a straightforward API.

Several bindings are available, including ones for Rust:

High-level, object-oriented bindings for C++, python3 and Rust are provided....
....
Rust bindings require cargo support. When building the Rust bindings along the
C library using make, they will be automatically configured to build against the
build results of the C library.

There's also gpiod:

Rust crate for interfacing with Linux GPIO character devices.
It provides an interface to the Linux GPIO using the chardev module. This interface involves calling ioctl funcions which are unsafe and require some unintuitive variable mapping. To ease this process, this crate provides a [Chip] struct which encapsulates the interface in safe Rust functions. The functionality provided here is highly inspired by libgpiod.

And gpiocdev:

A Rust library for accessing GPIO lines on Linux platforms using the GPIO character device.
This is the equivalent of libgpiod, but in pure Rust.

gpio-cdev warns against using this crate, but this crate makes no note of anything here, and implies that this API is still supported. This is not the case, and it should be updated to account for this.

I don't have much information on this aside from what I've noted here so far, so I probably can't help much for anything.

Pin number documentation

Not really blaming this crate, but elaborating on pin numbering might be useful to the documentation reader.

I connected physical pin 24 on my Raspberry Pi 3, and now in hindsight, I think that pin really should be accessed via Pin 8, still not sure as SPI CS doesn't work as expected.

Output of gpio readall:

+-----+-----+---------+------+---+---Pi 3---+---+------+---------+-----+-----+
| BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
+-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
|     |     |    3.3v |      |   |  1 || 2  |   |      | 5v      |     |     |
|   2 |   8 |   SDA.1 |   IN | 1 |  3 || 4  |   |      | 5v      |     |     |
|   3 |   9 |   SCL.1 |   IN | 1 |  5 || 6  |   |      | 0v      |     |     |
|   4 |   7 | GPIO. 7 |   IN | 1 |  7 || 8  | 0 | IN   | TxD     | 15  | 14  |
|     |     |      0v |      |   |  9 || 10 | 1 | IN   | RxD     | 16  | 15  |
|  17 |   0 | GPIO. 0 |   IN | 0 | 11 || 12 | 0 | IN   | GPIO. 1 | 1   | 18  |
|  27 |   2 | GPIO. 2 |   IN | 0 | 13 || 14 |   |      | 0v      |     |     |
|  22 |   3 | GPIO. 3 |   IN | 0 | 15 || 16 | 0 | IN   | GPIO. 4 | 4   | 23  |
|     |     |    3.3v |      |   | 17 || 18 | 1 | OUT  | GPIO. 5 | 5   | 24  |
|  10 |  12 |    MOSI | ALT0 | 0 | 19 || 20 |   |      | 0v      |     |     |
|   9 |  13 |    MISO | ALT0 | 0 | 21 || 22 | 0 | IN   | GPIO. 6 | 6   | 25  |
|  11 |  14 |    SCLK | ALT0 | 0 | 23 || 24 | 1 | OUT  | CE0     | 10  | 8   |
|     |     |      0v |      |   | 25 || 26 | 1 | OUT  | CE1     | 11  | 7   |
|   0 |  30 |   SDA.0 |   IN | 1 | 27 || 28 | 1 | IN   | SCL.0   | 31  | 1   |
|   5 |  21 | GPIO.21 |   IN | 1 | 29 || 30 |   |      | 0v      |     |     |
|   6 |  22 | GPIO.22 |   IN | 1 | 31 || 32 | 0 | IN   | GPIO.26 | 26  | 12  |
|  13 |  23 | GPIO.23 |   IN | 0 | 33 || 34 |   |      | 0v      |     |     |
|  19 |  24 | GPIO.24 |   IN | 0 | 35 || 36 | 0 | IN   | GPIO.27 | 27  | 16  |
|  26 |  25 | GPIO.25 |   IN | 0 | 37 || 38 | 0 | IN   | GPIO.28 | 28  | 20  |
|     |     |      0v |      |   | 39 || 40 | 0 | IN   | GPIO.29 | 29  | 21  |
+-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
| BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
+-----+-----+---------+------+---+---Pi 3---+---+------+---------+-----+-----+

So a clarification on what the Pin number refers to would be nice.

Doesn't build on OSX

Trying to build this package locally on a Macbook and I get the following error:

error[E0432]: unresolved import `nix::sys::epoll::*`
  --> src/lib.rs:64:5
   |
64 | use nix::sys::epoll::*;
   |     ^^^^^^^^^^^^^^^^^^^ Could not find `epoll` in `nix::sys`

Version Info:

$ rustc --version
rustc 1.13.0

cargo 0.13.0 (eca9e15 2016-11-01)

Write performance is slow

I'm using a Raspberry Pi 3 to write bits to a shift register. I use 2 pins: data and clock. At the rising edge of the clock, the shift register reads one bit from the data pin.

Here's a simple program that pushes 2 bits per loop [0, 1] to the shift register. Every second, it prints the number of loops it completed in the previous second.

fn main() {
    let clock = sysfs_gpio::Pin::new(20u64);
    let data = sysfs_gpio::Pin::new(21u64);
    match clock.with_exported(|| {
        data.with_exported(|| {
            sleep(Duration::from_millis(80));

            try!(clock.set_direction(sysfs_gpio::Direction::Out));
            try!(data.set_direction(sysfs_gpio::Direction::Out));

            println!("How fast can I tick-tock?");
            let start = std::time::Instant::now();
            let mut next_t = 1;
            let mut loops = 0;
            loop {
                try!(clock.set_value(0));
                try!(data.set_value(0));
                try!(clock.set_value(1));
                try!(clock.set_value(0));
                try!(data.set_value(1));
                try!(clock.set_value(1));

                loops += 1;
                let t = start.elapsed().as_secs();
                if t >= next_t {
                    println!("{}", loops);
                    next_t = t + 1;
                    loops = 0;
                }
            }
        })
    }) {
        Ok(()) => println!("Unreachable"),
        Err(err) => println!("main err: {}", err),
    }
}

Here's some output:

How fast can I tick-tock?
5095
5080
5087
5141
5133
5133
5141
5138

That's ~5130 loops/second, or ~10 Kbps. My requirement is 100 Mbps. The 1.2 GHz Pi 3 should be able to support this. Seems that rust-sysfs-gpio isn't the right tool for me, but I suspect there's an easy way to improve its performance by 1-2 orders of magnitude.

Level-triggered interrupt

The PinPoller interface appears to only support edge-triggered interrupts. Are there any plans to support level-triggering? It's the same code but without hardcoding the EPOLLET flag.

MSRV build on CI is broken due to dependencies

With Rust 1.31.1 the builds fail due to:

Compiling scopeguard v1.1.0
     Running `rustc --crate-name scopeguard /home/travis/.cargo/registry/src/github.com-1ecc6299db9ec823/scopeguard-1.1.0/src/lib.rs --color never --crate-type lib --emit=dep-info,link -C debuginfo=2 -C metadata=2c583fe15a6f6ee9 -C extra-filename=-2c583fe15a6f6ee9 --out-dir /home/travis/build/rust-embedded/rust-sysfs-gpio/target/debug/deps -L dependency=/home/travis/build/rust-embedded/rust-sysfs-gpio/target/debug/deps --cap-lints allow`
error[E0658]: use of unstable library feature 'try_from' (see issue #33417)
  --> /home/travis/.cargo/registry/src/github.com-1ecc6299db9ec823/byteorder-1.4.2/src/lib.rs:74:5
   |
74 |     convert::TryInto, fmt::Debug, hash::Hash, ptr::copy_nonoverlapping, slice,
   |     ^^^^^^^^^^^^^^^^
   Compiling slab v0.4.2
     Running `rustc --crate-name slab /home/travis/.cargo/registry/src/github.com-1ecc6299db9ec823/slab-0.4.2/src/lib.rs --color never --crate-type lib --emit=dep-info,link -C debuginfo=2 -C metadata=aeeeff3fc23ce2a0 -C extra-filename=-aeeeff3fc23ce2a0 --out-dir /home/travis/build/rust-embedded/rust-sysfs-gpio/target/debug/deps -L dependency=/home/travis/build/rust-embedded/rust-sysfs-gpio/target/debug/deps --cap-lints allow`

error[E0658]: use of unstable library feature 'int_to_from_bytes' (see issue #52963)
    --> /home/travis/.cargo/registry/src/github.com-1ecc6299db9ec823/byteorder-1.4.2/src/lib.rs:1959:9
     |
1959 |         u16::from_be_bytes(buf[..2].try_into().unwrap())
     |         ^^^^^^^^^^^^^^^^^^

Integrate mio support

I believe it would be beneficial to add support for mio (poll multiple FDs). This should be possible with conditional compilation.

Bad performance

Hi,
many functions (e.g. Pin::set_direction, Pin::set_value, Pin::get_value, Pin::set_edge, Pin::get_poller) take longer than somebody would expect. Making them unsuitable for many tasks. The reason for the bad performance is that the sysfs-gpio-files direction,edge, and value are opened on demand and closed afterwards. This makes it impossible to read a sensor like the DHT22 (https://github.com/Filkolev/DHT22-sensor-driver) or performing any time critical operation. Whereas a C/C++/Python/... application would be able to do so.
Code example:

let pin = Pin::new(17);
pin.set_direction(Direction::Out);
pin.set_value(1);
[...]
pin.set_direction(Direction::In);
pin.set_edge(Edge::BothEdges);
let mut poller = pin.get_poller().unwrap();
// At this point I already have lost parts of the sensor reply
// if I use pin.get_value() I only receive 1/8 of the desired values
[...]

At the moment I don't see any way to fix this issue without breaking the API.
Any suggestions?

Best regards,
Bernd

Fix CI

We need to fix the CI so that we can merge new PRs.
Inspiration can be drawn from here.

Migrate to tokio 1.x

Since the kernels supporting only this interface and not gpio-cdev will continue with us "forever", it would be great to give this crate a revamp and migrate it to tokio 1.x.
However, I do not have the know-how to do this.
Could somebody help us here?
Maybe @benjumanji, @oll3 or somebody else?

Over at gpio-cdev is an example of the migration: rust-embedded/gpio-cdev#55

Permission denied on first run

Hello, I've a problem in Heimdall. When I first run it, I get permission denied, but second time it works. I guess this is because export doesn't show up in /sys/ immediately. (I tried to unexport instead of rebooting and it panics too.)

Did you encounter this problem too? Is there any plan to fix it?

non async io in async code

I want to ask why you have non sync operation from fs like write_all read_to_string
Looks like you need use tokio::fs

Or if it is ok, can you please tell why it is so

Export a SysfsGpioError type

The public API for this crate currently returns Result<_, std::error::Error>. This makes composing errors from this crate into a higher level error type difficult since we can not use the usual std::convert::From and try! idioms.

I propose a SysfsGpioError type is added and returned as the Error type for methods returning a Result. Not having though about it much yet, it might look something like this.

// Assuming these are all wrapping a lower level error of some sort, otherwise they
// could just take a string or nothing (add description in the Error impl).

#[derive(Debug)]
pub enum SysfsGpioError {
    SetDirection(Error),
    SetValue(Error),
    Export(Error),
    Unexport(Error),
    // etc
}

And then consumers can easily impl From<SysfsGpioError> for their error type.

Assuming this is something you're interested in, and that you don't get to it first, I'll put a PR up soon.

Thanks!

Make operations on pins take mutable reference

I believe it'd be more logical to take mutable reference to pins. I know that memory itself doesn't change, however, there are good reasons to require mutability anyway:

  • Accessing the pin from multiple threads probably isn't what people want to do by accident. (They still can do it on purpose.)
  • Requiring mutable reference allows people to write mocks and simulate the pins in tests.
  • It's more natural to use mutable references when you change something.
  • It clearly shows that some state is being mutated.

What do you think?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.