write: /sys/class/gpio/export: device or resource busy
Opened this issue · 5 comments
I am trying to get simple gpio working on my rpi model B.
From the gopath/src/github.com/kidoman/embd/samples
directory I run go build gpio.go
and sudo ./gpio
.
I get quick panic with panic: write: /sys/class/gpio/export: device or resource busy
Running latest raspbian image. I have built go from source on the device.
Can you try with sudo
?
// +build ignore
package main
import (
"flag"
"github.com/kidoman/embd"
"time"
_ "github.com/kidoman/embd/host/all"
)
func main() {
flag.Parse()
if err := embd.InitGPIO(); err != nil {
panic(err)
}
defer embd.CloseGPIO()
led, err := embd.NewDigitalPin(17)
if err != nil {
panic(err)
}
defer led.Close()
if err := led.SetDirection(embd.Out); err != nil {
panic(err)
}
for i := 0; i < 10; i++ {
if err := led.Write(embd.High); err != nil {
panic(err)
}
time.Sleep(1 * time.Second)
if err := led.Write(embd.Low); err != nil {
panic(err)
}
}
if err := led.SetDirection(embd.In); err != nil {
panic(err)
}
}
I can reproduce that with this code and before finish, I do Ctrl + c.
When I run again:
pi@raspberrypi ~/rpi/blink_led $ sudo ./main
panic: write /sys/class/gpio/export: device or resource busy
goroutine 1 [running]:
main.main()
/home/pi/rpi/blink_led/main.go:27 +0x1a8
goroutine 17 [syscall, locked to thread]:
runtime.goexit()
/home/pi/go/src/runtime/asm_arm.s:1014 +0x4
goroutine 5 [chan receive]:
github.com/golang/glog.(*loggingT).flushDaemon(0x1ec590)
/home/pi/gocode/src/github.com/golang/glog/glog.go:879 +0x60
created by github.com/golang/glog.init.1
/home/pi/gocode/src/github.com/golang/glog/glog.go:410 +0x2cc
goroutine 8 [runnable]:
github.com/kidoman/embd/host/generic.initEpollListener.func1(0x1040e5f0)
/home/pi/gocode/src/github.com/kidoman/embd/host/generic/interrupt.go:56
created by github.com/kidoman/embd/host/generic.initEpollListener
/home/pi/gocode/src/github.com/kidoman/embd/host/generic/interrupt.go:70 +0x1cc
somehow restarting fixed my problem. I was also mucking about in raspi-config too, so not sure if anything there helped.
I think if I exit without closing down gpio it can leave stuff around that is hard to clean up. I can catch interrupts and execute a close before exiting, but I am not sure it is a good idea for anything outside of main to do this.
Two options:
1)
root@raspberrypi:~# echo 10 > /sys/class/gpio/unexport
- where 10 GPIO10.
You have to handle the signal in a goroutine and then switch on it in a loop, making the program exit cleanly and run it's defer's.
package main
import (
"os"
"os/signal"
"syscall"
"fmt"
//"time"
"github.com/kidoman/embd"
_ "github.com/kidoman/embd/host/rpi" // This loads the RPi driver
)
// For cleanup...
func cleanup() <- chan bool {
exitProg := make(chan bool)
//exitProg <- false
signalChannel := make(chan os.Signal, 2)
signal.Notify(signalChannel, os.Interrupt, syscall.SIGTERM)
fmt.Println("Setting up SIG")
go func() {
sig := <-signalChannel
switch sig {
case os.Interrupt:
//handle SIGINT
fmt.Println("\nSIGINT caught")
case syscall.SIGTERM:
//handle SIGTERM
fmt.Println("\nSIGTERM caught")
}
exitProg <- true
fmt.Println("Received an interrupt...\n")
}()
return exitProg
}
func main() {
// Main program
if err := embd.InitGPIO(); err != nil {
fmt.Println("Error during GPIO init!")
panic(err)
}
defer embd.CloseGPIO()
// Setup LED
pinNo := 10
led, err := embd.NewDigitalPin(pinNo)
if err != nil {
fmt.Println("Error setting NewDigitalPin (10)!")
panic(err)
}
defer led.Close()
if err := led.SetDirection(embd.Out); err != nil {
fmt.Println("Error in SetDirection (10)!")
panic(err)
}
// Setup BTN
btn, err := embd.NewDigitalPin(22)
if err != nil {
fmt.Println("Error in NewDigitalPin (22)!")
panic(err)
}
defer btn.Close()
if err := btn.SetDirection(embd.In); err != nil {
fmt.Println("Error in SetDirection (22)!")
panic(err)
}
///btn.ActiveLow(false)
// Listen for ntn press
btnEvent := make(chan int)
err = btn.Watch(embd.EdgeBoth, func(btn embd.DigitalPin) {
btnVal, _ := btn.Read()
//fmt.Println(btnVal)
btnEvent <- btnVal
})
if err != nil {
panic(err)
}
exitProg := cleanup()
// Run forever
fmt.Println("BTNLED demo...")
doExit := false
btnVal := 0
for !doExit {
select {
case doExit = <-exitProg:
case btnVal = <-btnEvent:
//fmt.Println(e)
if btnVal == embd.High {
if err := led.Write(embd.High); err != nil {
fmt.Println("Error writing High to GPIO", pinNo, "!")
panic(err)
}
} else {
if err := led.Write(embd.Low); err != nil {
fmt.Println("Error writing Low to GPIO", pinNo, "!")
//panic(err)
}
}
}
}
// Close and cleanup
if err := led.SetDirection(embd.In); err != nil {
panic(err)
}
}
I ran into this same issue on a Raspberry Pi 2 Model B. Following @akofoed's instructions, I was able to get the program to run multiple times in a row without any more errors:
package main
import (
"log"
"os"
"os/signal"
"syscall"
"time"
"github.com/kidoman/embd"
_ "github.com/kidoman/embd/host/rpi" // This loads the RPi driver
"golang.org/x/net/context"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
sigch := make(chan os.Signal, 2)
signal.Notify(sigch, os.Interrupt, syscall.SIGTERM)
go handleSignals(sigch, ctx, cancel)
log.Println("hello world!")
embd.InitGPIO()
defer embd.CloseGPIO()
pin, err := embd.NewDigitalPin("GPIO_5")
if err != nil {
log.Fatal("opening pin:", err)
}
defer resetPin(pin)
if err = pin.SetDirection(embd.Out); err != nil {
log.Fatal("setting pin direction:", err)
}
nextValHigh := true
for {
select {
case <-ctx.Done():
return
case <-time.After(500 * time.Millisecond):
if nextValHigh {
pin.Write(embd.High)
} else {
pin.Write(embd.Low)
}
nextValHigh = !nextValHigh
}
}
}
func handleSignals(sigch <-chan os.Signal, ctx context.Context, cancel context.CancelFunc) {
select {
case <-ctx.Done():
case sig := <-sigch:
switch sig {
case os.Interrupt:
log.Println("SIGINT")
case syscall.SIGTERM:
log.Println("SIGTERM")
}
cancel()
}
}
func resetPin(pin embd.DigitalPin) {
if err := pin.SetDirection(embd.In); err != nil {
log.Fatal("resetting pin:", err)
}
pin.Close()
}
What do you think about a PR to add this cleanup to the Raspberry Pi examples (including the readme)? That, or this should be handled automatically by the cleanup/Close()
functions for either the GPIO or the pin. Are there cases where people don't want those pins to be cleaned up after the program exits?