go-gpiocdev

repository·master·Indexed 19 days ago

https://github.com/warthog618/go-gpiocdev

A native Go library for interacting with Linux GPIO pins via the GPIO character device. It provides a Go-idiomatic equivalent to the C libgpiod library, featuring direction control, active high/low states, output modes, pull-up/pull-down resistors (Linux 5.5+), edge detection, and debouncing (Linux 5.10+). The library is thread-safe and includes a low-level uapi package for direct access to Linux GPIO ioctl system calls.

Tokens
8.9K
Snippets
21
Records
36
Agent score
67%

What's inside go-gpiocdev

  1. Overview of gpiocdev

    master

    gpiocdev is a native Go library designed for accessing GPIO pins (lines) on Linux platforms using the GPIO character device. It provides functionality equivalent to the C libgpiod library, allowing developers to control GPIO hardware directly from Go applications.

    Key features include:

    • Direction control (input/output)
    • Reading and writing line values (active/inactive)
    • Configuring active high/low states
    • Setting output modes (push-pull, open-drain, open-source)
    • Configuring pull-up/pull-down resistors (Linux 5.5+)
    • Edge detection and watches (rising, falling, or both)
    • Debouncing input lines (Linux 5.10+)
    • Support for chip and line labels
    • Thread-safety: All library functions are safe to call from different goroutines.
  2. Understand the GPIOCDEV UAPI

    master

    The uapi package is a thin Go layer over the Linux kernel's GPIO ioctl system calls. It provides direct access to the Linux GPIO User API (UAPI).

    Important Note: This library is a low-level interface. For most general-purpose use cases, you should use the higher-level gpiocdev package, which provides better abstractions. Use uapi primarily if you need minimal overhead or are performing low-level testing of the UAPI itself.

  3. Quick Start: Read an input and write to an output

    master

    To use gpiocdev, you can request specific lines from a GPIO chip. In this example, we read the value of an input line (pin 2 on gpiochip0) and immediately write that value to an output line (pin 3 on gpiochip0).

    Note: For production code, you must implement proper error handling and ensure you release resources (close lines/chips) when finished.

    import "github.com/warthog618/go-gpiocdev"
    
    ...
    
    // Request pin 2 as an input
    in, _ := gpiocdev.RequestLine("gpiochip0", 2, gpiocdev.AsInput)
    
    // Read the current value
    val, _ := in.Value()
    
    // Request pin 3 as an output, setting its initial value to the value read from pin 2
    out, _ := gpiocdev.RequestLine("gpiochip0", 3, gpiocdev.AsOutput(val))
    
    ...
  4. Use gpiocdev-cli for manual GPIO manipulation

    master
    For manual or scripted manipulation of GPIO lines without writing Go code, use the companion package gpiocdev-cli. It combines the functionality of all libgpiod command-line tools into a single utility.
  5. Run library tests with gpio-sim

    master

    The library includes a comprehensive test suite. To run these tests, you must satisfy these conditions:

    1. Kernel: Requires Linux kernel 5.19 or later.
    2. Kernel Configuration: Must be built with CONFIG_GPIO_SIM enabled or as a module.
    3. Privileges: Tests must be run as root to allow the construction of gpio-sim instances.

    You can build the test binary as an unprivileged user, but execution requires root.

    Cross-compiling tests for Raspberry Pi:

    # For Raspberry Pi (ARMv6)
    $ GOOS=linux GOARCH=arm GOARM=6 go test -c
    
    # For later Raspberry Pis (ARMv7)
    $ GOOS=linux GOARCH=arm GOARM=7 go test -c
    # Build tests (unprivileged)
    $ go test -c
    
    # Run tests (must be root)
    $ sudo ./go-gpiocdev.test
  6. Watch for edge events on input lines

    master

    You can monitor an input line for rising, falling, or both edges using an edge watch. This is configured by providing an event handler via the WithEventHandler(eh) option during the line request.

    Implementation Details

    • The handler function receives a LineEvent containing the line offset, detection time, and edge type.
    • Concurrency Warning: The event handler is called serially from a goroutine reading from the kernel. The handler should be short-lived; hand off blocking operations to a separate goroutine to avoid stalling the event loop.
    • Closing: To stop watching, call l.Close() or reconfigure the line with gpiocdev.WithoutEdges.
    • Safety: Close() waits for the handler to return, so do not call Close() from within the handler context; call it from a different goroutine.
    func handler(evt gpiocdev.LineEvent) {
      // handle edge event (keep this short!)
    }
    
    // Request line with edge detection
    l, _ = c.RequestLine(rpi.J8p7, gpiocdev.WithEventHandler(handler), gpiocdev.WithBothEdges)
    
    // To stop watching
    l.Reconfigure(gpiocdev.WithoutEdges)
  7. Initialize a GPIO Chip

    master

    The Chip object is the entry point for discovering available lines and requesting them.

    • Use gpiocdev.NewChip(name) to create a chip. The name corresponds to the device in /dev/ (e.g., "gpiochip0" for /dev/gpiochip0).
    • Use gpiocdev.Chips() to list all currently available GPIO chips.
    • You can set default attributes for all lines requested from this chip using options like WithConsumer(label).
    • Always call c.Close() when the chip is no longer needed. Note that closing a chip does not close or alter the state of lines already requested from it.
    // List available chips
    cc := gpiocdev.Chips()
    
    // Initialize a specific chip with a consumer label
    c, _ := gpiocdev.NewChip("gpiochip0", gpiocdev.WithConsumer("myapp"))
    
    // Release chip resources
    c.Close()
  8. Verify Linux kernel and device prerequisites

    master

    Before using the library, ensure your system meets the following requirements:

    • GPIO Character Device: The /dev/gpiochipX device (e.g., /dev/gpiochip0) must exist.
    • Permissions: The caller must have access to the character device, which typically requires root privileges unless permissions have been manually modified.
    • Kernel Version Requirements:
      • Linux 5.5+: Required for using Bias line options and the Line.Reconfigure method.
      • Linux 5.10+: Required for Debounce and other uAPI v2 features.
  9. Install go-gpiocdev

    master

    To use this library in your Go project on Linux, use go get to fetch the module. If you are cross-compiling for Linux from a different platform, use the -d flag to download the package without attempting to compile it for your current host architecture.

    # Standard installation on Linux
    go get github.com/warthog618/go-gpiocdev
    
    # Installation for cross-compilation
    go get -d github.com/warthog618/go-gpiocdev
  10. Monitor GPIO line events using EventHandler

    master

    To monitor GPIO line events (such as edge detection), you must provide an implementation of the EventHandler interface. When an event occurs on a monitored line, the watcher calls your handler with a LineEvent object containing the event details.

    Note: The watcher and watcherV1 types are unexported (private). You likely interact with them through higher-level exported functions in the package (such as those that initialize a chip or line) which accept an EventHandler to set up monitoring. The LineEvent structure provides the following data:

    • Offset: The line offset where the event occurred.
    • Timestamp: The time the event occurred (as a time.Duration).
    • Type: The type of event (e.g., rising or falling edge).
    • Seqno / LineSeqno: Sequence numbers for event tracking (available in V2).

    To stop monitoring and release resources, call the Close() method on your watcher instance.

    // Example of what an EventHandler implementation might look like
    type myHandler struct{}
    
    func (h *myHandler) HandleEvent(le gpiocdev.LineEvent) {
    	fmt.Printf("Event on line %d: type=%v, timestamp=%v\n", le.Offset, le.Type, le.Timestamp)
    }
  11. Handle line events with an EventHandler

    master

    To receive notifications when a line changes state, provide an EventHandler using WithEventHandler(e EventHandler).

    Important Considerations:

    • Serial Execution: The event handler is called serially for each event from the requested lines to maintain ordering.
    • Performance: The handler should process the event or hand it off to another goroutine and return as quickly as possible to avoid overflowing the kernel event queue.
    • Deadlock Warning: Do not call Close() on a requested line from within its own event handler. Close() waits for the handler to return, which will cause a deadlock. Call Close() from a different goroutine.
    // Example EventHandler
    // handler := func(ev gpiocdev.LineEvent) {
    //     fmt.Printf("Event on line %d\n", ev.Offset)
    // }
    // 
    // lines, _ := chip.RequestLines([]int{0}, gpiocdev.WithEventHandler(handler))
  12. Configure Line properties with LineConfig

    master

    The LineConfig struct defines how a line behaves. Key fields include:

    • ActiveLow: If true, the logic is inverted.
    • Direction: LineDirectionInput or LineDirectionOutput.
    • Drive: LineDrivePushPull, LineDriveOpenDrain, or LineDriveOpenSource.
    • Bias: LineBiasDisabled, LineBiasPullUp, or LineBiasPullDown.
    • EdgeDetection: LineEdgeNone, LineEdgeRising, LineEdgeFalling, or LineEdgeBoth.
    • Debounced: Boolean indicating if hardware/software debouncing is used.
    • DebouncePeriod: Duration for debouncing.
    • EventClock: LineEventClockMonotonic or LineEventClockRealtime.