set_async_interrupt not working
JohnRudolfLewis opened this issue · 22 comments
I'm no rust expert, but I think this should work....
the println in the set_async_interrupt handler never fires.
the println in the loop faithfully reports the correct level of the pin.
Am I doing something wrong? If so, please provide a valid example in the docs.
This is on a Raspberry Pi Zero W
use std::{thread, time};
use rppal::gpio::Gpio;
use rppal::gpio::Trigger;
use rppal::gpio::Level;
fn main() {
let gpio = Gpio::new().expect("Gpio new");
let pin = gpio.get(12).expect("gpio get");
let mut pin = pin.into_input_pullup();
pin.set_async_interrupt(Trigger::Both, move |level: Level|{
println!("This never works!! {}", level);
}).unwrap();
loop {
thread::sleep(time::Duration::from_millis(1000));
println!("button is {}.", pin.read());
}
}
Thanks for reporting this issue. At first glance, I don't see any obvious problems in the way you're working with async interrupts. I'm going to try this out here as well and see what I can find.
FYI: Same behavior observed when compiling using https://github.com/rust-embedded/cross from my laptop as from when compiling directly on the Pi Zero.
I just tried the code you provided without any modifications, on a Raspberry Pi 3 A+, and both the foreground loop and the background thread correctly displayed any level changes from high to low, and low to high.
I'm not sure why this doesn't work in your situation. Some things to check would be making sure you're running the latest release of Raspbian, having PWM disabled (pwm0 uses the same GPIO pin) or trying a different GPIO pin.
For the sake of completeness, I ran your code on a Raspberry Pi Zero W as well (compiled on the device) without any issues, getting the expected results. The Zero is running a linux kernel from 2017, so it doesn't seem to be related to changes in any recent Raspbian releases.
I am in the middle of upgrading my Pi Zero W to the latest Raspbian. I will comment again once re-tested on latest.
The same pin works with some python code... lots snipped out but here is the meat of it... but this code is normally running all the time on this Pi....
import RPi.GPIO as GPIO
BUTTON_PIN = 12
def button_event(channel):
global start
global pressed
if GPIO.input(BUTTON_PIN) == 1:
pressed = False
if GPIO.input(BUTTON_PIN) == 0:
start = time.time()
pressed = True
GPIO.setmode(GPIO.BCM)
GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(BUTTON_PIN, GPIO.BOTH, callback=button_event)
So I don't believe that there is an issue with using this pin. I stopped that Python code while running the Rust code.
This Pi is running reef-pi (implemented in Go) to control my saltwater reef tank. It is possible that something else in that software is in conflict. I will shut that code down to see if behavior changes.
I'll comment again with the next set of results.
It could be possible for another process to grab the events from the underlying /dev/gpiochipN
device before the Rust code has a chance to get them, so trying the code without having anything else running could be interesting. Looking forward to hearing about your progress.
Running latest Raspbian and not running either the Python or Go code mentioned above... same behavior, never see the background thread being triggered.
The Python and Go code still continues to run correctly.
I suggest trying a different pin just to make sure there's nothing else interfering with the one you're currently using. Or if you have another Pi lying around, try running the code on there. Other than that, at the moment I don't know what else you could try.
I can't reproduce the problem on my devices, and considering you're not running into the same problem with Python or Go, it doesn't appear to be a hardware issue. My best guess would be there is still something else interfering on the software side, perhaps reading the interrupt events and/or not properly closing its access to the driver.
I will comment again if I can think of any other possible causes. Please let me know if you end up figuring out what's happening, or have any tips on how to reproduce it on another device.
Actually, one more possible cause comes to mind. Make sure the relevant pin isn't exported in /sys/class/gpio
. The sysfs gpio interface is deprecated and shouldn't be used anymore, but some libraries still haven't moved on to the newer /dev/gpiochipN
interface. While I haven't tried this in practice, it seems reasonable to assume having the pin exported through the sysfs interface while trying to access it through /dev/gpiochipN
might cause issues.
ls /sys/class/gpio
export gpio10 gpio11 gpio12 gpio17 gpio22 gpio27 gpio5 gpio6 gpio9 gpiochip0 unexport
Aha! That means either Python or Go accesses the GPIO pins through the sysfs interface, and doesn't unexport the pins after the process exits. Could you try running echo 12 > /sys/class/gpio/unexport
which unexports the pin and removes gpio12
from that directory (check to make sure it's really gone), and then run your Rust code again?
That fixes it.. thank you
Now I get to figure out which piece of code exported that.
Awesome! Glad we found the cause. When you do figure out which code still uses the sysfs gpio interface, I suggest either finding a different library that uses /dev/gpiochipN
instead, or adding some code that unexports the used pins when the process exits. The sysfs gpio interface will be removed from linux eventually, so it's a good idea to move on to the newer interface whenever you're able to.
It was the RPi.GPIO python library that was the culprit. I'll be temporarily adding a GPIO.cleanup() on exit to clean up after that code so that I can develop/test a replacement in rust using rppal.
Thank you for all the help in troubleshooting.
Hey! I had the same issue; no interrupts triggering. There weren't any exported GPIOs in my sys/class/gpio
directory either. I had to set set_reset_on_drop(false)
to get the pullup resistor working (and therefore the rest of the interrupt/level reading code):
use std::{thread, time};
use rppal::gpio::Gpio;
use rppal::gpio::Trigger;
use rppal::gpio::Level;
fn main() {
let gpio = Gpio::new().expect("Gpio new");
let pin = gpio.get(26).expect("gpio get");
let mut pin = pin.into_input_pullup();
pin.set_reset_on_drop(false);
pin.set_async_interrupt(Trigger::Both, move |level: Level|{
println!("This never works!! {}", level);
}).unwrap();
loop {
thread::sleep(time::Duration::from_millis(1000));
println!("button is {}.", pin.read());
}
}
Any idea why this is? I'm still fairly new to Rust. I would guess that the pin is going out of scope when we set the interrupt (hence, the move
keyword). Is this accurate? Is there a better way to write this code in which the set_reset_on_drop()
function isn't required? Thanks for your help!
Update: for some reason, that exact code stopped working. Again, same issue: it doesn't appear that internal pull-up resistors are being set (verified with a multimeter). Any suggestions would be appreciated!
Thanks for the report, @bblanke. I'm not quite sure why you're seeing this result. The pin can't go out of scope until after the loop, otherwise you wouldn't be able to read its value with read
. I'll recreate your setup, and will let you know what I find.
Considering it used to work for you with the example you posted, and now doesn't anymore, it sounds to me like something external is interfering, but if you're not running any other software that interacts with the GPIO pins, and it isn't a hardware issue (try a different GPIO pin), we'll have to dig a little deeper. 😄
I have run into maybe the same issue here as well? Couldn't find any GPIO pin which would fire on an interrupt, ended up just polling the pin instead...
Funny story, I was looking at other code I had with this library and realized I have another processes running on the same Raspberry Pi which is using this library (maybe an older version I would have to check) and using interrupts and it works just fine!
Is it possible two separate processes using this same library to setup interrupts is not possible on the same device? Just a thought, thanks for the sweet library!
I'm running into the same issue here. The interrupt callback is never triggered. I did verify that there are no exported GPIO pins in /sys/class/gpip
. There's only export
, unexport
and gpiochip0
.
I don't know if this is relevant, but on other pins I do have an NFC reader running via SPI.
Here's my /boot/config.txt
, if that helps to narrow the issue down:
dtparam=spi=on
dtoverlay=gpio-no-irq
dtoverlay=hifiberry-dac
dtoverlay=i2s-mmap
Hi guys,
I am currently trying to resolve exactly same problem as you: I can successfully reading the pin state in a loop but when I am using set_async_interrupt
then the callback is never called :(
I am using Raspberry Pi Zero 2 W and the system is a bare buildroot-ed environment (it is not a Raspberry PI OS).
I am sure that no other process is touching the GPIOs in any way as I am controlling all executables on this Pi.
For testing I am using GPIO pin no 4, rppal crate is rppal "0.19.0"
.
Do you have any clue why it is not working? Is there some specific tested configuration (eg GPIO pin which is working for you)?
Edit: I downgraded crate to 0.15.0
to make sure that it was not regression - but it doesn't work there either
Update: I was able to make this working but on another raspberry pi 2 w which is running Raspberry Pi OS.
I did it with the following code: https://gist.github.com/manio/b612d5a16d3acd7a17eccda14ddd7377
I am still investigating...
Update: I figured my problem out! :)
I was creating Gpio::new, pin and set_async_interrupt
in a scope. When the program goes out of this scope it was just not working. So my solution was to declare it upper in main
fn with:
+ let g;
+ let mut pin;
if run_mode == RunMode::Client {
[...]
Now the trigger is called properly :)