I’ve working on adding some physical controls to my kitchen status display. The setup is very simple, and for a first-run all I wanted to be able to do was detect a button press, and run a script somewhere on the system.
I couldn’t find any existing code that does this (please point some out if you know of any), though there’s plenty of code out there for button handling in general.
Also, most of the code out there for doing GPIO work is either in C or Python, and I was hoping for a Ruby solution, or at least a solution that would be easy to wrap in Ruby.
In the end I learned about how to interface with the GPIO system on RPi via the filesystem.
From there, I found this code from Aaron Patterson using epoll
to monitor for changes in the level on the monitored pin. That seemed like it would work, but the epoll gem seems to be unmaintained at this point. Instead, I went with the Sleepy Penguin gem, which provides an interface to the same underlying Linux filesystem events.
Lastly, I wanted my pin pulled high by default, and there’s no way to do that via Sysfs, so I ended up using the gpio
utility to set the pin state and pull up resistor.
From there, I was able to run this little script, which will output the current state of the pin when it changes.
# install gpio command line tool
# gpio -g mode 16 up
require 'sleepy_penguin'
def watch pin, on:
puts "Setup Export"
File.binwrite "/sys/class/gpio/export", pin.to_s
puts "Setup Edge"
retries = 0
begin
File.binwrite "/sys/class/gpio/gpio#{pin}/edge", on
rescue
raise if retries > 3
sleep 0.1
retries += 1
retry
end
puts "Read Value"
fd = File.open "/sys/class/gpio/gpio#{pin}/value", 'r'
yield fd.read.chomp
puts "Setup epoll"
epoll = SleepyPenguin::Epoll.new
epoll.add fd, SleepyPenguin::Epoll::PRI
puts "Start loop wait"
loop do
fd.seek 0, IO::SEEK_SET
epoll.wait do
# yield fd.read.chomp
value = fd.read.chomp
p value
end
end
ensure
File.binwrite "/sys/class/gpio/unexport", pin.to_s
end
pin = 16
watch pin, on: 'both' do |value|
p value
end
Still to come: debouncing, better handling of a change event, and hopefully wrapping it all up into something much easier to work with.