Following on to my last post about GPIO on the Raspberry PI, I continued my exploration to reading and listening to value changes on a GPIO pin.
I found that the PI4J library was not REPL friendly. I could close a port and shutdown my use of the
GpioFactory, but my listeners wouldn't be cleaned up. Once I created a listener once, that listener was stuck (and orphaned). Looking at the changes in PI4J's git repo, this looks fixed on a development branch, but far from release.
I decided to circle back and start from scratch. Here's where clj-gpio was born.
Writing to the ports was relatively easy - simply writing the correct to the various
/sys/class/gpio files. Reading the current value was easy, too (yet another file read).
Things get a little more hairy when you need to be notified that the file has changed. One can poll for changes, but this isn't very timely, nor is it very efficient.
So what we need is an OS-level mechanism for watching for file changes. Enter
epoll. This is a linux system call, so it's understandable that we don't have access to it through the JDK.
Using JNA, we can access these system calls directly (of course only on systems where they are available) with little effort - no JNI needed. Also, thanks to Clojure's Java interopt, using this small set of classes is relatively straight-forward. Coupling this with core.async, and now we have a very simple gpio pin listening tool.
Now, in our REPL, we can do the following:
user=> (def ch-port (open-channel-port 18)) #'user/ch-port user=> (set-direction! ch-port :in) nil user=> (set-edge! ch-port :both) ; or :falling, :rising, and :none to disable nil user=> (def ch (create-edge-channel ch-port)) #'user/ch user=> (require '[clojure.core.async :as a :refer [go <!]]) nil user=> (go (loop  (when-let [value (<! ch)] (println value) (recur)))) #<ManyToManyChannel clojure.core.async.impl.channels.ManyToManyChannel@1197ad0>
When we're done with our listener and our port we'll can just do
user=> (release-edge-channel! ch-port ch) nil user=> (close! ch-port) nil
This can be done repeatably with in a single REPL session.
Up next for this little library? PWM!