Skip to content

Instantly share code, notes, and snippets.

@thekvs
Created October 13, 2017 05:43
Show Gist options
  • Save thekvs/e1d01af937e70cc4c30f92bb24ae6634 to your computer and use it in GitHub Desktop.
Save thekvs/e1d01af937e70cc4c30f92bb24ae6634 to your computer and use it in GitHub Desktop.
package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"os/signal"
"runtime"
"strconv"
"golang.org/x/sys/unix"
)
const (
kMaxEpollEvents = 32
kRPiGPIOPathTemplate = "/sys/class/gpio/gpio%d/value"
)
var GPIOSystemId = []int{5, 6, 13, 19, 26, 21, 20, 16}
type GPIOCtx struct {
fh *os.File
path string
id int
}
func initGPIOMonitoring(epfd int, ids []int) map[int]*GPIOCtx {
contexts := make(map[int]*GPIOCtx)
var event unix.EpollEvent
for _, e := range ids {
path := fmt.Sprintf(kRPiGPIOPathTemplate, GPIOSystemId[e])
fh, err := os.Open(path)
if err != nil {
log.Fatalf("Couldn't open GPIO file '%v': %v", path, err)
}
fd := int(fh.Fd())
if err = unix.SetNonblock(fd, true); err != nil {
log.Fatalf("setnonblock: %v", err)
}
contexts[fd] = &GPIOCtx{
fh: fh,
path: path,
id: e,
}
event.Events = (unix.EPOLLET & 0xffffffff) | unix.EPOLLPRI | unix.EPOLLERR
event.Fd = int32(fd)
if err = unix.EpollCtl(epfd, unix.EPOLL_CTL_ADD, fd, &event); err != nil {
log.Fatalf("epoll_ctl: %v", err)
}
}
return contexts
}
func cleanup(contexts map[int]*GPIOCtx) {
for _, ctx := range contexts {
ctx.fh.Close()
}
log.Println("Done!")
}
func readGPIOValue(ctx *GPIOCtx) {
data, err := ioutil.ReadFile(ctx.path)
if err != nil {
log.Fatalf("Failed to read GPIO value from file '%v': %v", ctx.path, err)
}
log.Printf("%v %v", ctx.id, string(data))
}
func main() {
log.SetFlags(log.Ldate | log.Lmicroseconds)
flag.Parse()
if flag.NArg() == 0 {
flag.Usage()
os.Exit(1)
}
epfd, err := unix.EpollCreate1(0)
if err != nil {
log.Fatalf("EpollCreate1() failed: %v", err)
}
defer unix.Close(epfd)
ids := make([]int, flag.NArg())
for idx, e := range flag.Args() {
id, err := strconv.Atoi(e)
if err != nil {
log.Fatalf("Invalid GPIO id: %v", err)
}
if id < 0 || id >= len(GPIOSystemId) {
log.Fatalf("GPIO id %v out of range", id)
}
ids[idx] = id
}
contexts := initGPIOMonitoring(epfd, ids)
defer cleanup(contexts)
go func() {
runtime.LockOSThread()
var events [kMaxEpollEvents]unix.EpollEvent
for {
nevents, err := unix.EpollWait(epfd, events[:], -1)
if err != nil {
if err == unix.EAGAIN || err == unix.EINTR {
continue
}
log.Fatalf("EpollWait error: %v", err)
}
for ev := 0; ev < nevents; ev++ {
if (events[ev].Events & unix.EPOLLPRI) != 0 {
readGPIOValue(contexts[int(events[ev].Fd)])
}
}
}
}()
signalChannel := make(chan os.Signal, 2)
signal.Notify(signalChannel, os.Interrupt, unix.SIGTERM)
receivedSignal := <-signalChannel
log.Printf("Got signal: %v. Exiting.", receivedSignal)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment