rust-embedded / gpio-cdev Goto Github PK
View Code? Open in Web Editor NEWRust interface to the Linux GPIO Character Device API (/dev/gpiochip...)
License: Apache License 2.0
Rust interface to the Linux GPIO Character Device API (/dev/gpiochip...)
License: Apache License 2.0
How do you extend the gpioevents.rs example to handle multiple input pins using event handlers?
EDIT: To clarify, I don't really want to have to follow the poll methodology in monitor.rs as polling IO pins is inefficient and prone to missing changes in IO state.
-Andy.
I am fairly new to Rust, and particularly to embedded Rust, so there's a good chance this is just my mistake.
I am running the following:
#[tokio::main]
async fn main() {
let mut chip = Chip::new("/dev/gpiochip0").unwrap();
let line = chip.get_line(10).unwrap();
let _trigger_task = tokio::task::spawn(async move {
let mut events = AsyncLineEventHandle::new(line.events(LineRequestFlags::INPUT, EventRequestFlags::FALLING_EDGE, "input_trigger").unwrap()).unwrap();
while let Some(_event) = events.next().await {
println!("Trigger");
}
}
);
}
And it panics with:
There is no reactor running, must be called from the context of a Tokio 0.2.x runtime
.
I replaced the code inside the closure with a simple print statement, and it worked fine suggesting the issue is in the GPIO stuff.
Am I just being stupid? I would really appreciate any help!
Travis is shutting down and running a build to completion is already quite impossible.
Hello,
When using the following snippet in my code (as part of a Wiegand reader program) on a Raspberry Pi 0:
let monitor_0: Next<AsyncLineEventHandle> = events0.next();
let monitor_1: Next<AsyncLineEventHandle> = events1.next();
pin!(monitor_0, monitor_1);
select! {
res = monitor_0 => { data = data << 1; },
res = monitor_1 => { data = (data << 1) | 1 as u64; },
}
it seems like the code ends up in a Deadlock in the select! macro after running this snippet for 128 times, after which the program uses 100% CPU but neither of the Next
s ever completes.
events0
and events1
are AsyncLineEventHandle
s that come from Line
s that come from the same Chip
.
Introducing a millisecond delay at the very top increases the amount of bytes that can be read to 256.
Introducing a larger delay seems to remove the deadlock all together, but the ordering of the events is lost causing the data to become garbage as the order of the events determines the final data output.
I'm not certain if this is a gpio-cdev problem, a kernel problem, or if I'm simply doing this incorrectly.
Any feedback is highly appreciated.
Installation instructions in the readme uses version 0.2, but it looks like 0.2 is not published on crates.io
Updating crates.io index
error: failed to select a version for the requirement `gpio-cdev = "^0.2"`
candidate versions found which didn't match: 0.1.0
location searched: crates.io index
Any chance this crate can be updated on crates.io?
Hi! Do you have a plan, in the near future, to implement the access of multiple bits simultaneously? If not, do you want me to take a crack at it? I'm going to need this within the next few weeks.
Impossible to use anyhow
with gpio-cdev
:
error[E0277]: `(dyn std::error::Error + std::marker::Send + 'static)` cannot be shared between threads safely
--> src/main.rs:9:52
|
9 | let mut chip = gpio_cdev::Chip::new(RELAY_CHIP)?;
| ^ `(dyn std::error::Error + std::marker::Send + 'static)` cannot be shared between threads safely
|
= help: the trait `std::marker::Sync` is not implemented for `(dyn std::error::Error + std::marker::Send + 'static)`
= note: required because of the requirements on the impl of `std::marker::Sync` for `std::ptr::Unique<(dyn std::error::Error + std::marker::Send + 'static)>`
= note: required because it appears within the type `std::boxed::Box<(dyn std::error::Error + std::marker::Send + 'static)>`
= note: required because it appears within the type `std::option::Option<std::boxed::Box<(dyn std::error::Error + std::marker::Send + 'static)>>`
= note: required because it appears within the type `error_chain::State`
= note: required because it appears within the type `gpio_cdev::errors::Error`
= note: required because of the requirements on the impl of `std::convert::From<gpio_cdev::errors::Error>` for `anyhow::Error`
= note: required by `std::convert::From::from`
There is no mention of AsyncLineEventHandle
anywhere on docs.rs. The only way to see this documentation is to build it locally with cargo doc
, which is a bit of a pain, especially since many of the people using gpio-cdev
are working on a raspberry pi, and if they are running the lite version of Raspbian, without a GUI, as I am, there is no browser, so cargo doc --open
won't work. We need to manually go in and copy the docs directory to a different computer to read it.
When I did cargo doc
on my project, it generated 16k files. Using scp to copy them over to my other computer was going to take like half an hour. I had to interrupt the scp copy operation, tar the 16k of doc html files, transfer it as one file, and unarchive it before opening it. The whole process took like 20 minutes. It would have been nice if I could have just found the documentation on docs.rs in seconds, as I do with most other documentation.
Great library! It got me up and running on the new chardev gpio devices which was fantastic. I've been experimenting with using Rust on Linux SBCs. However the library isn't suitable for cross compiling currently; would it be possible to remove the extra C library dependencies?
My embedded device build system uses cross compiling for everything and the backtrace
dependency (via both the error-chain
and clap
dependencies) makes cross compiling a pain. To get it to function correctly you have to use an obscure environment variable CARGO_TARGET_$(TRIPLET)_LINKER=
to setup the appropriate cross linker.
Effectively the addition of a C library in the Rust toolchain eliminates almost all of the advantages of using Rust. It add unnecessary complications in a cross compiling scenario as now I have 2 more compilers settings to get correct (the Rust one, and the c compiler). Is it possible to remove the backtrace or use a Rust native one?
Want to say thanks to the team behind this library, but as a newbie to embedded programming how do i use this to control a servo?
I know the char driver(s) expose bits as u8
values, but it might be easier to manipulate the values as booleans. Should we consider:
impl LineHandle {
pub fn get_value(&self) -> Result<bool> { ... }
pub fn set_value(&self, value: bool) -> Result<()> { ... }
...
Is there an easy way to configure a PWM using this crate? If not, does anyone know a good crate for doing so?
Version 0.2.0 has a dependency on nix
version 0.11.0, which does not compile on the Raspberry Pi. This is fixed in nix
version 0.14.0. See nix-rust/nix#1057
Tokio 1 with long stability guarantee is out. It would be nice if at some point gpio-cdev
updates to this version.
LineEventHandle
seems to be dropping events, as evident in the repro below, but also in a more optimized repro just filtering for FALLING_EDGES
and knowing the min/max rate that these events are emitted, versus the actual interval between events.
The following snippet run on a Raspberry Pi Zero (ARM32).
for e in Chip::new("/dev/gpiochip0")?
.get_line(pin)?
.events(
LineRequestFlags::INPUT,
EventRequestFlags::BOTH_EDGES,
"read",
)? {
println!("{}", e.event_type());
}
LineEventHandle
yields multiple events of the same type in sequence.
LineEventHandle
strictly alternates between each type.
Is there support for electrical configuration of GPIO pins (e.g. enabling pull-up resistors on the Raspberry Pi)?
If I look at https://www.kernel.org/doc/html/v5.3/driver-api/gpio/driver.html#gpio-electrical-configuration this should be possible, right? Or is that a different API? (I'm still figuring out the kernel GPIO APIs, so forgive me if I'm mixing up things ๐)
It has been 2 years since it released last time. Could we make a new release?
Create a new project foo
with gpio-cdev
as a dependency with the async-tokio feature.
[dependencies]
gpio-cdev = { version = "0.3", features = ["async-tokio"] }
This gives us an error
... the package `foo` depends on `gpio-cdev`, with features: `async-tokio` but `gpio-cdev` does not have these features.
I want to use this driver to see if it is more performant than sysfs. The one problem I'm running into is there is no gpiochip0
listed under /dev
.
How do I add gpiochip0
to /dev
so I can start using this library?
Thanks so much for all your hard work and help! Go rust!
I'm trying to follow the mutliread.rs example but when I try to compile it then I get the following error:
error[E0599]: no method named get_lines found for type std::result::Result<gpio_cdev::Chip, gpio_cdev::errors::Error> in the current scope
I'm new to Rust so don't know exactly what I am doing and why this is going wrong!
-Andy.
Could an example be shown for doing an async multiread? I don't see a lot of things i can do with get_lines
such as events()
and events_async()
Hello,
Thank you for providing us with this wonderful library!
A missing feature of this library to me is the ability to do a non-blocking poll on the LineEventHandle struct.
Currently every method (.get_event()
, .next()
and .read_event()
) is blocking.
Ideally we would have a sort of .has_event() -> bool
function available to check whether there is an event available to process. This way we would take maximum advantage of the queue-like architecture of the new GPIO character device ABI.
Please let me know what you think of this suggestion.
Cheers,
Sven.
Just looking for a bit of clarification here.
From what I understand, this is how it works:
Importantly, this queue is per-process.
As in, when our Rust application starts up, and uses gpio_cdev
to register to be notified about a pin state change with:
let handle = chip.get_line(N)?.request(LineRequestFlags::INPUT, 0, "read-input")?;
The kernel sets up a queue specifically for this process, for this one line.
The alternative, which I'm really hoping isn't the case, is that there's one queue per line that the kernel maintains, and when your Rust application starts, and registers to be notified, the queue may already have events in it that may potentially need to be discarded.
Is it possible to use gpio-cdev
to change the peripheral function of a GPIO PIN?
For context, I'm using gpio-cdev
on a Raspberry PI to turn GPIO PIN 2 into output mode and send a wake pulse to a chip connected via I2C. Unfortunately, calling chip.get_line(2).request(LineRequestFlags::OUTPUT, 1, "test")
changes the mode of the GPIO PIN to output and I have not yet found a way of "resetting" the PIN to its original state after completing the wake pulse.
I found, however, I can reset it manually from the command line using sudo raspi-gpio set 2 a0
but I was wondering if the same could be accomplished from within gpio-cdev
or if it could be extended to allow for that.
CI is currently broken, also we need to migrate to GitHub MQ.
See #72
torvalds/linux@b53911a introduces the uapi v2 which has slightly different ioctl calls. Also v1 is deprecated from that point on: torvalds/linux@b234d23
Is there anything that needs doing before bumping the version?
Hi, I've successfully used the streaming interface using the request
method:
fn do_main(args: Cli) -> errors::Result<()> {
let mut chip = Chip::new("/dev/gpiochip7")?;
let lines = vec![0, 1, 2, 3, 4, 5];
let initial_values = vec![0; lines.len()];
loop {
let handle = chip.get_lines(&lines)?.request(
LineRequestFlags::INPUT,
&initial_values,
"multiread",
)?;
println!("Values: {:?}", handle.get_values()?);
thread::sleep(time::Duration::from_millis(10));
}
}
When I try to use a blocking iterator however (see code below), I get the following error:
Error: Error(Msg("lineevent ioctl failed"), State { next_error: Some(Sys(ENODEV)), backtrace: InternalBacktrace { backtrace: None } })
.
ENODEV
would appear to be a device driver issue ("the corresponding device driver does not support the ioctl() function.") but I'm not clear on that.
Has anyone bumped into this issue before?
Failing code:
fn do_main(args: Cli) -> errors::Result<()> {
let mut chip = Chip::new("/dev/gpiochip7")?;
let input = chip.get_line(0)?;
// Show all state changes for this line forever
for event in input.events(
LineRequestFlags::INPUT,
EventRequestFlags::BOTH_EDGES,
"rust-gpio",
)? {
println!("{:?}", event?);
}
Ok(())
}
I think it would be amazing to be able to compile to WASM + WASI, so that we can compile code, that has only access to given devices. In my understanding, it should be possible, since /dev/gpiochipX are handled as files** and could be preopened via fs over WASI. I'm a total noob though, so I don't know if that's even possible.
Following, some interesing infos I've found:
wasm3
runtime that use WASI with Arduinos by passing the needed functions into WASM.Currently building to target wasm32-wasi
results in several errors.
** on Linux
Hello!
First, thanks a lot for creating this crate and the work you put into it.
I have a small suggestion that could improve the documentation. Consider the following program which is a simplified version of the driveoutput.rs
example:
use gpio_cdev::{Chip, LineRequestFlags};
fn main() {
let mut chip = Chip::new("/dev/gpiochip0").unwrap();
chip
.get_line(4).unwrap()
.request(LineRequestFlags::OUTPUT, 1, "set-pin").unwrap();
println!("Press Enter to exit");
let mut buf = String::new();
::std::io::stdin().read_line(&mut buf).unwrap();
}
This program compiles and does not panic, but seemingly does nothing as the pin remains in an unset state while the program waits on the user to press Enter
. However, if we assign the LineHandle
instance to a variable like this:
let _handle = chip
.get_line(4).unwrap()
.request(LineRequestFlags::OUTPUT, 1, "set-pin").unwrap();
Then the pin state is correctly set, presumably because the file descriptor contained inside the LineHandle
doesn't immediately get dropped after request
is called.
I think that it is important to note in the comments that the LineHandle
needs to remain in scope or assigned to a persistent variable to prevent these sorts of "silent failures." It may be obvious in the above example, but in my case I do some initialization of the chip and store it inside a struct that is passed around; it took me about a day of debugging to realize that I needed initialize and store the LineHandle
instance in addition to the Chip
.
If you agree, then I will open a small PR to update the examples and docstrings to make note of this.
Line.request()
accepts LineRequestFlags
including ACTIVE_LOW
. This inverts the polarity of the value in the LineHandle::get_value()
and LineHandle::set_value()
APIs to mean:
0
-> inactive -> electrically high1
-> active -> electrically lowLine.request()
also accepts a default
argument which is currently documented as:
For an output, the
default
parameter specifies the value the line should have when it is configured as an output.
The polarity of this argument is not clear.
If I do this:
line.request(LineRequestFlags.ACTIVE_LOW, 0, "me");
does 0
mean:
Somewhat related: #49
Does it matter what we set this arg for an input line? If not, can we get a short note in the docs that it doesn't matter. Maybe have it be an Option<u8>
that can be set to None
?
I've tried to run readall.rs example on orange pi zero, but app panicked with the message "panicked at 'index out of bounds: the len is 64 but the index is 64', .\gpio-cdev-0.2.0\src\lib.rs:766:13"
The problem is that Lines struct in my case contains more than 64(GPIOHANDLES_MAX) lines.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.