Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GPIO monitoring with select

Tags:

linux

ruby

gpio

I am attempting to monitor a GPIO pin, and per the Linux docs I should be able to do this by monitoring the /sys/class/gpio/gpio##/value file with select:

"value" ... reads as either 0 (low) or 1 (high).  If the GPIO
    is configured as an output, this value may be written;
    any nonzero value is treated as high.

    If the pin can be configured as interrupt-generating interrupt
    and if it has been configured to generate interrupts (see the
    description of "edge"), you can poll(2) on that file and
    poll(2) will return whenever the interrupt was triggered. If
    you use poll(2), set the events POLLPRI and POLLERR. If you
    use select(2), set the file descriptor in exceptfds. After
    poll(2) returns, either lseek(2) to the beginning of the sysfs
    file and read the new value or close the file and re-open it

I am attempting to do this in Ruby, and per the IO.Select documentation it calls select(2).

So, with this knowledge I threw together the following test program:

fd = File.open("/sys/class/gpio/gpio17/value", "r")

loop do
  rs,ws,es = IO.select(nil, nil, [fd], 5)
  if es
    r = es[0]
    puts r.read(1)
  else
    puts "timeout"
  end
end

However, it does not detect any pin changes. When I launch this app it will immediately fall into the if block and display the pin's current value, and then every 5 seconds just prints timeout.

Have I read the docs wrong? Shouldn't select be able to monitor this?

like image 618
Jason Whitehorn Avatar asked Jan 12 '13 06:01

Jason Whitehorn


1 Answers

Before select will correctly trigger on a GPIO pin you'll need to setup the pin's edge trigger. From the GPIO docs:

"edge" ... reads as either "none", "rising", "falling", or
    "both". Write these strings to select the signal edge(s)
    that will make poll(2) on the "value" file return.

    This file exists only if the pin can be configured as an
    interrupt generating input pin.

In Ruby simply:

File.open("/sys/class/gpio/gpio17/edge", "w") { |f| f.write("both") }

The complete example from above would look like:

fd = File.open("/sys/class/gpio/gpio17/value", "r")
File.open("/sys/class/gpio/gpio17/edge", "w") { |f| f.write("both") }

loop do
  rs,ws,es = IO.select(nil, nil, [fd], 5)
  if es
    r = es[0]
    puts r.read(1)
  else
    puts "timeout"
  end
end
like image 162
Jason Whitehorn Avatar answered Oct 09 '22 09:10

Jason Whitehorn