Git Product home page Git Product logo

rust-sysfs-gpio's Issues

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.

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

Fix CI

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

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?

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

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.

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

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.

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 ?

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.

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

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)

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.

Integrate mio support

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

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?

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?

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!

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())
     |         ^^^^^^^^^^^^^^^^^^

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!

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.

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.