Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

write: /sys/class/gpio/export: device or resource busy #30

Open
captncraig opened this issue Jun 8, 2015 · 5 comments
Open

write: /sys/class/gpio/export: device or resource busy #30

captncraig opened this issue Jun 8, 2015 · 5 comments

Comments

@captncraig
Copy link

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.

@kidoman
Copy link
Owner

kidoman commented Jun 9, 2015

Can you try with sudo ?

@gmonnerat
Copy link


// +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

@captncraig
Copy link
Author

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.

@akofoed
Copy link

akofoed commented Jul 11, 2015

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)
    }
}

@bgentry
Copy link

bgentry commented Sep 5, 2015

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants