2

I have a GPIO pin, that value of which is represented in the sysfs node /sys/class/gpio/gpioXXXX/value) and I want to detect a change to the value of this GPIO pin. According to the sysfs documentation you should use poll(2) or select(2) for this.

However, both poll and message only seems to be available as a system calls and not from bash. Is there some way to use to get triggered by a state change of the GPIO pin functionality from a bash script?

My intention is to not have (semi-)busy waiting or userland polling. I would also like to simply do this from bash without having to dip into another language. I don't plan to stick with bash throughout the project, but I do want to use it for this very first version. Writing a simple C program to be called from bash for just this is a possibility, but before doing that, I would like to know if I'm not missing something.

Jasper
  • 11,590
  • 6
  • 38
  • 55
  • 1
    Short form: You're not missing something; `poll()` isn't directly available without using external software (or writing a loadable module for bash adding an additional built-in command). – Charles Duffy Nov 22 '16 at 17:24
  • ...that said, if performance is a primary goal, I might suggest that you might consider dropping bash. It's certainly possible to write much more efficient shell scripts than most people do (and there are certainly much faster shells than bash), but there's still an upper limit on how performant they'll ever be, and that upper limit is still waaaay below the point where worrying about what kind of interrupt is in use is relevant. – Charles Duffy Nov 22 '16 at 17:26
  • I don't have advice on how to use `poll()`, but if this gpio value is visible at the filesystem level, might `tail -F` send the change to stdout? If so, a super simple solution like [this](http://stackoverflow.com/a/10958125/1072112) might do. – ghoti Nov 22 '16 at 17:34
  • If what you want is "a way to monitor a file for changes" that *isn't* specific to gpio, then what you get is a completely different answer focused around inotify-tools. Is the question gpio-specific, or is it not? – Charles Duffy Nov 22 '16 at 21:49
  • Absolutely does make a difference. Even `poll()` doesn't work for GPIO without non-default flags. – Charles Duffy Nov 22 '16 at 22:16
  • @CharlesDuffy I have rewritten the question. I'll remove some of my comments that don't add much anymore. – Jasper Nov 22 '16 at 22:44
  • Is the C helper I've built (and linked to from my answer) helpful? – Charles Duffy Nov 22 '16 at 23:02
  • @CharlesDuffy Definitely. I would probably have gone with a `call -> trigger -> return` strategy instead of a loop that keeps outputting to stdout if I'd have had to write it myself, but this is probably better for performance. – Jasper Nov 22 '16 at 23:18
  • Also reduces the chances of missing an event that happens between the return and the next call. – Charles Duffy Nov 22 '16 at 23:22

1 Answers1

3

Yes, you'll need a C or Python helper -- and you might think about abandoning bash for this project entirely.

See this gist for an implementation of such a helper (named "wfi", "watch-for-interrupt", modified from a Raspberry Pi StackExchange question's answer.


That said:

If you want to (semi-)efficiently have a shell script monitor for a GPIO signal change, you'll want to have a C helper that uses poll() and writes to stdout whenever a noteworthy change occurs. Given that, you can then write a shell loop akin to the following:

while IFS= read -r event; do
  echo "Processing $event"
done < <(wfi /sys/class/gpio/gpioXXXX/value)

Using process substitution in this way ensures that the startup cost for your monitor-gpio-signal helper is paid only once. Note some caveats:

  • Particularly if anything inside the body of your loop calls an external command (rather than relying on shell builtins alone), this is still going to be much slower than using a program written in C, Go or even an otherwise-relatively-slow language such as Python.

  • If the shell script isn't ready to receive a write, that write may block for an indefinite amount of time. A tool such as pv may be useful to add a buffer to your pipeline:

    done < <(wfi "name" | pv -q -B 1M)
    

    ...for instance, will establish a 1MB buffer.

Community
  • 1
  • 1
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • I edited out the part about performance (replacing it by something that more accurately covers what I meant with it). This answer is definitely is still interesting, but performance really isn't this much of a challenge, but I won't let it drive the entire design as you did here. – Jasper Nov 22 '16 at 21:32