Evented GPIO on Raspberry PI with Ruby
Jan 17, 2017 @ 10:24 amI need to know when my cats are pooping so over the weekend I hooked up a motion sensor to my Raspberry PI. This is the code I used to get an interrupt when the motion sensor turns on or off:
require 'epoll'
def watch pin, on:
# Export the pin we want to watch
File.binwrite "/sys/class/gpio/export", pin.to_s
# It takes time for the pin support files to appear, so retry a few times
retries = 0
begin
# `on` should be "none", "rising", "falling", or "both"
File.binwrite "/sys/class/gpio/gpio#{pin}/edge", on
rescue
raise if retries > 3
sleep 0.1
retries += 1
retry
end
# Read the initial pin value and yield it to the block
fd = File.open "/sys/class/gpio/gpio#{pin}/value", 'r'
yield fd.read.chomp
epoll = Epoll.create
epoll.add fd, Epoll::PRI
loop do
fd.seek 0, IO::SEEK_SET
epoll.wait # put the program to sleep until the status changes
yield fd.read.chomp
end
ensure
# Unexport the pin when we're done
File.binwrite "/sys/class/gpio/unexport", pin.to_s
end
pin = 5
watch pin, on: 'both' do |value|
p value
end
Whenever an event happens on the GPIO pin, the block will be executed. I want the block to be executed when the sensor detects movement and when it detects no movement (if you imagine that as a wave, I want to know about the rising and falling edges), so I passed “both” to the watch
function.
I am very new to developing on Raspberry PI, and I’m not sure what people normally use for Ruby + GPIO on Raspberry PI. I looked at the rpi_gpio gem. It gives access to read values of GPIO, but doesn’t give you any events. In other words, you can use it to read the current value of a pin, but it won’t let you know when the value of a pin has changed. It looks like there is code to support this, but it’s not fully hooked up yet. I noticed that the C code is just using Epoll, so I tried using the epoll gem, and it works.
The rpi_gpio
gem is cool because it allows your program to read from a pin without echoing to “exports” and reading from a file. The gem just mmap
s a special device, and then reads from memory. Unfortunately, it doesn’t seem like there is a way to generate “on change” events with that system. That means we have to write to the “export” file and run poll
on the “value” file. As you can see from the example above, waiting for events (the thing we want to do) accounts for only a few lines of code where managing export / value files accounts for most of the function.
I am new to Raspberry PI development, so maybe there is an easier way, but I haven’t found it. At least this works so I can know when my cats are pooping.
The End.