Git Product home page Git Product logo

gpio's Introduction

GPIO

This is a Go library for general purpose pins on Linux devices which support /sys/class/gpio. This implementation conforms to the spec. An advantage of using /sys/class/gpio is that we can receive interrupt-like notifications from the kernel when an input changes, rather than polling an input periodically. See the notes on Watcher for more info.

I have only tested it so far on Raspberry Pi but it should also work on similar systems like the Beaglebone. If you test this library on another system please tell me so that I can confirm it -- I'll give you credit here for the confirmation.

Note that the GPIO numbers we want here as the CPU/kernel knows them, not as they may be marked on any external hardware headers.

Input

Call pin := gpio.NewInput(number) to create a new input with the given pin numbering. You can then access the value of this pin with pin.Read(), which returns 0 when the pin's value is logic low and 1 when high.

If you are only concerned with when the pin's value changes, consider using gpio.Watcher instead.

Output

Call pin := gpio.NewOutput(number, high), where high is a bool that describes the initial value of the pin -- set to false if you'd like the pin to initialize low, and true if you'd like it to initialize high.

Once you have a pin, you can change its value with pin.Low() and pin.High().

Watcher

The Watcher is a type which listens on the GPIO pins you specify and then notifies you when the values of those pins change. It uses a select() call so that it does not need to actively poll, which saves CPU time and gives you better latencies from your inputs.

Here is an example of how to use the Watcher.

watcher := gpio.NewWatcher()
watcher.AddPin(22)
watcher.AddPin(27)
defer watcher.Close()

go func() {
    for {
        pin, value := watcher.Watch()
        fmt.Printf("read %d from gpio %d\n", value, pin)
    }
}()

This example would print once each time the value read on either pin 22 or 27 changes. It also prints each pin once when starting.

Alternately, users may receive from watcher.Notification directly rather than calling watcher.Watch(). This channel yields WatcherNotification objects with Pin and Value fields.

License

3-clause BSD

gpio's People

Contributors

andreacioni avatar brian-armstrong avatar joeblubaugh avatar olb17 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

gpio's Issues

Non-blocking way to catch the pin change events ?

watcher.Watch() is good, but it blocks the program execution. Is there a non-blocking way to catch the pin change events ?

Can watcher.Notification do this ? If yes, could you give me some example code ? I am very new to golang. Thanks.

Issues on RPI3B+

Is anyone aware of breaking changes between using this library on a RPI 3B compared to a RPI 3B+? We have strange behavior sometimes (yes, really strange) when controlling a RPI relay board https://www.exp-tech.de/module/relais/7442/rpi-relay-board via this library. It might be changes to the /sys directory which only apply to RPI3, but I am not quite sure. We run our software in docker containers on rpi and the only difference really is RPI3B+ instead of RPI3B. Any help appreciated!

Wrong permissions + Timing issue in NewOutput/NewInput

Hi @brian-armstrong,

I'm start testing your work on Beaglebone Black, I'm getting the following error: failed to open gpio 60 direction file for writing. I've changed last parameter in all occurences of os.OpenFile in sys.fs to 0666 (r/w for everyone) and everything works. I'm using it as a non-root user but my account belongs to gpio group.

Andrea Cioni

Issues on Pi Zero W

Hi Brian,

to start, I would like to thank you for providing this library, your work is much appreciated! The approach of using the GPIO sysfs interface and a blocking select() call seems more appropriate to me than polling, as it is done in other GPIO implementations.

I use your lib in a private project, in which I am building an audio box, controllable via web app and also physical buttons, running on a Raspberry Pi Zero W.

However, I experience issues when adding new Pins to a button upon a fresh reboot or with unexported pins (via AddPin() with watcher receiver). The behaviour is reproducible. By extending the error printing in function exportGPIO() for debugging purposes, I could see that writing into the direction file is not working, when the issue occurs:

failed to open gpio 2 direction file for writing open /sys/class/gpio/gpio2/direction: permission denied

Restarting the application again the same way as before, the pin can then be added properly. The same goes for additional subsequent starts of the application. (I ruled out any actual permission issues of the user running the application).

Eventually I figured out that the duration of 10ms between calling exportGPIO() and setDirection() seems to be too short for a Pi Zero. Increasing this duration to 100ms, the issue does not occur anymore. So, I suppose that the Pi Zero is simply too slow in order to prepare the GPIO sysfs interface after exporting the pin and subsequently creating the direction file with proper permissions. So the main issue can be described as a race condition.

My actual issue with the described behaviour is that setDirection() calls os.Exit(1) when os.OpenFile() returns an error. I don't want my service to Exit(1), when the direction can't be set. I would rather handle the error and retry adding the pin. I use supervisord to restart my application a few times until all Pins are added and proper configured, however this seems as an ugly workaround for me.

I propose two possible solutions for the issue.

1.) Quick and dirty: Increase sleep duration between exportGPIO() and setDirection() to 100ms (?). Another approach but in the same direction could be providing the option in the lib to give the desired waiting duration externally via parameter to AddPin().

2.) More elaborate: Remove os.Exit(1) call from setDirection() (and ideally the whole lib) and return the error upstream to the exported function (in this case from setDirection() via exportGPIO() and NewInput() to AddPin(), in order to handle the error in the calling application. In case AddPin() fails, the application can then handle the error properly, i .e. retry adding in this case.

If you need more info, just let me know.

Best regards,

Jens

De-jankify select() call

The merging of posix-style select() call with Go seems pretty jank, at least in this implementation. The select() has a 1-second timeout, so that if there are no pins changing, it takes up to a second to add and remove new pins to the watcher https://github.com/brian-armstrong/gpio/blob/master/watcher.go#L112 and https://github.com/brian-armstrong/gpio/blob/master/watcher.go#L193

We should investigate if it's possible to remove these in some way without impacting CPU.

One possible option, though sort of gross, would be to open a file in /tmp (or a socket) and have our select() call include it in its readfds. Then a goroutine could write to this file descriptor in order to wake up the select() caller, which could then do maintenance like adding/removing pins or closing. If we did this, we could possibly remove the 1-second timeout entirely, allowing us to sleep more while having very good latency for these sorts of operations.

Issue with watcher on Pi Zero

Hello Brian,

I’m using the latest Raspbian image on Raspberry Pi Zero + Zero Ws, I can read a pin manually without issue, but I haven’t gotten any action out of the watcher. I have two questions:

  1. Has the library been tested on the Zero / Zero W before? If not, is there any reason to think it might not be compatible?

  2. I’m using BCM pin 7 (physical pin 26) as an input. As far as I understood, the kernel uses BCM numbering; pin := gpio.NewInput(7) works, so is there any reason why watcher.AddPin(7) wouldn’t?

Thanks for your help!

Other sysfs peripherals

Hi Brian
Thanks for your work! I appreciate very much this project.
I'm preparing to test your library on Beaglebone Black and UDOO. Hope this help 😊

Just a question, did you plan to add also some other peripherals (PWM for example)? I can try to do that, but I'm not sure you want such things in your project (name of your repo is gpio). Let me know

Cheers,
Andrea Cioni

it's works on NenoPi NEO2

Hi Brian
Thanks for your work! I appreciate very much this project.
I'm have tested your library on NanoPi NEO2. It's works!

Thank you!

Please forgive me for using the Issues to leave a message for you.

GPIO0-GPIO7 Always High

Because GPIO0 through (and including) GPIO7 are set as Pull-Up (always High) by default, you have to connect them to GND to switch them from 1 to 0.

You cannot set them Low().

Support/expose channels

Instead of calling a blocking function, listening to a channel would be better.

We could select between a gpio event and something else.

openPin() architect improvement

in
func openPin(p Pin, write bool) Pin {
}

It would be better not to call fmt.Printf() and os.Exit(1) inside openPin()
instead please return p, err

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.