hybridgroup.gobot/system/digitalpin_config.go

259 lines
9.3 KiB
Go

package system
import (
"time"
"gobot.io/x/gobot/v2"
)
const (
// IN gpio direction
IN = "in"
// OUT gpio direction
OUT = "out"
// HIGH gpio level
HIGH = 1
// LOW gpio level
LOW = 0
)
const (
digitalPinBiasDefault = 0 // GPIO uses the hardware default
digitalPinBiasDisable = 1 // GPIO has pull disabled
digitalPinBiasPullDown = 2 // GPIO has pull up enabled
digitalPinBiasPullUp = 3 // GPIO has pull down enabled
// open drain and open source allows the connection of output ports with the same mode (OR logic)
// * for open drain/collector pull up the ports with an external resistor/load
// * for open source/emitter pull down the ports with an external resistor/load
digitalPinDrivePushPull = 0 // the pin will be driven actively high and low (default)
digitalPinDriveOpenDrain = 1 // the pin will be driven active to low only
digitalPinDriveOpenSource = 2 // the pin will be driven active to high only
digitalPinEventNone = 0 // no event will be triggered on any pin change (default)
digitalPinEventOnFallingEdge = 1 // an event will be triggered on changes from high to low state
digitalPinEventOnRisingEdge = 2 // an event will be triggered on changes from low to high state
digitalPinEventOnBothEdges = 3 // an event will be triggered on all changes
)
const (
// DigitalPinEventRisingEdge indicates an inactive to active event.
DigitalPinEventRisingEdge = "rising edge"
// DigitalPinEventFallingEdge indicates an active to inactive event.
DigitalPinEventFallingEdge = "falling edge"
)
type digitalPinConfig struct {
label string
direction string
outInitialState int
activeLow bool
bias int
drive int
debouncePeriod time.Duration
edge int
edgeEventHandler func(lineOffset int, timestamp time.Duration, detectedEdge string, seqno uint32, lseqno uint32)
pollInterval time.Duration
pollQuitChan chan struct{}
}
func newDigitalPinConfig(label string, options ...func(gobot.DigitalPinOptioner) bool) *digitalPinConfig {
cfg := &digitalPinConfig{
label: label,
direction: IN,
}
for _, option := range options {
option(cfg)
}
return cfg
}
// WithPinLabel use a pin label, which will replace the default label "gobotio#".
func WithPinLabel(label string) func(gobot.DigitalPinOptioner) bool {
return func(d gobot.DigitalPinOptioner) bool { return d.SetLabel(label) }
}
// WithPinDirectionOutput initializes the pin as output instead of the default "input".
func WithPinDirectionOutput(initial int) func(gobot.DigitalPinOptioner) bool {
return func(d gobot.DigitalPinOptioner) bool { return d.SetDirectionOutput(initial) }
}
// WithPinDirectionInput initializes the pin as input.
func WithPinDirectionInput() func(gobot.DigitalPinOptioner) bool {
return func(d gobot.DigitalPinOptioner) bool { return d.SetDirectionInput() }
}
// WithPinActiveLow initializes the pin with inverse reaction (applies on input and output).
func WithPinActiveLow() func(gobot.DigitalPinOptioner) bool {
return func(d gobot.DigitalPinOptioner) bool { return d.SetActiveLow() }
}
// WithPinPullDown initializes the pin to be pulled down (high impedance to GND, applies on input and output).
// This is working since Kernel 5.5.
func WithPinPullDown() func(gobot.DigitalPinOptioner) bool {
return func(d gobot.DigitalPinOptioner) bool { return d.SetBias(digitalPinBiasPullDown) }
}
// WithPinPullUp initializes the pin to be pulled up (high impedance to VDD, applies on input and output).
// This is working since Kernel 5.5.
func WithPinPullUp() func(gobot.DigitalPinOptioner) bool {
return func(d gobot.DigitalPinOptioner) bool { return d.SetBias(digitalPinBiasPullUp) }
}
// WithPinOpenDrain initializes the output pin to be driven with open drain/collector.
func WithPinOpenDrain() func(gobot.DigitalPinOptioner) bool {
return func(d gobot.DigitalPinOptioner) bool { return d.SetDrive(digitalPinDriveOpenDrain) }
}
// WithPinOpenSource initializes the output pin to be driven with open source/emitter.
func WithPinOpenSource() func(gobot.DigitalPinOptioner) bool {
return func(d gobot.DigitalPinOptioner) bool { return d.SetDrive(digitalPinDriveOpenSource) }
}
// WithPinDebounce initializes the input pin to be debounced.
func WithPinDebounce(period time.Duration) func(gobot.DigitalPinOptioner) bool {
return func(d gobot.DigitalPinOptioner) bool { return d.SetDebounce(period) }
}
// WithPinEventOnFallingEdge initializes the input pin for edge detection and call the event handler on falling edges.
func WithPinEventOnFallingEdge(handler func(lineOffset int, timestamp time.Duration, detectedEdge string, seqno uint32,
lseqno uint32),
) func(gobot.DigitalPinOptioner) bool {
return func(d gobot.DigitalPinOptioner) bool {
return d.SetEventHandlerForEdge(handler, digitalPinEventOnFallingEdge)
}
}
// WithPinEventOnRisingEdge initializes the input pin for edge detection and call the event handler on rising edges.
func WithPinEventOnRisingEdge(handler func(lineOffset int, timestamp time.Duration, detectedEdge string, seqno uint32,
lseqno uint32),
) func(gobot.DigitalPinOptioner) bool {
return func(d gobot.DigitalPinOptioner) bool {
return d.SetEventHandlerForEdge(handler, digitalPinEventOnRisingEdge)
}
}
// WithPinEventOnBothEdges initializes the input pin for edge detection and call the event handler on all edges.
func WithPinEventOnBothEdges(handler func(lineOffset int, timestamp time.Duration, detectedEdge string, seqno uint32,
lseqno uint32),
) func(gobot.DigitalPinOptioner) bool {
return func(d gobot.DigitalPinOptioner) bool {
return d.SetEventHandlerForEdge(handler, digitalPinEventOnBothEdges)
}
}
// WithPinPollForEdgeDetection initializes a discrete input pin polling function to use for edge detection.
func WithPinPollForEdgeDetection(
pollInterval time.Duration,
pollQuitChan chan struct{},
) func(gobot.DigitalPinOptioner) bool {
return func(d gobot.DigitalPinOptioner) bool {
return d.SetPollForEdgeDetection(pollInterval, pollQuitChan)
}
}
// SetLabel sets the label to use for next reconfigure. The function is intended to use by WithPinLabel().
func (d *digitalPinConfig) SetLabel(label string) bool {
if d.label == label {
return false
}
d.label = label
return true
}
// SetDirectionOutput sets the direction to output for next reconfigure. The function is intended to use
// by WithPinDirectionOutput().
func (d *digitalPinConfig) SetDirectionOutput(initial int) bool {
if d.direction == OUT {
// in this case also the initial value will not be written
return false
}
d.direction = OUT
d.outInitialState = initial
return true
}
// SetDirectionInput sets the direction to input for next reconfigure. The function is intended to use
// by WithPinDirectionInput().
func (d *digitalPinConfig) SetDirectionInput() bool {
if d.direction == IN {
return false
}
d.direction = IN
return true
}
// SetActiveLow sets the pin with inverse reaction (applies on input and output) for next reconfigure. The function
// is intended to use by WithPinActiveLow().
func (d *digitalPinConfig) SetActiveLow() bool {
if d.activeLow {
return false
}
d.activeLow = true
return true
}
// SetBias sets the pin bias (applies on input and output) for next reconfigure. The function
// is intended to use by WithPinPullUp() and WithPinPullDown().
func (d *digitalPinConfig) SetBias(bias int) bool {
if d.bias == bias {
return false
}
d.bias = bias
return true
}
// SetDrive sets the pin drive mode (applies on output only) for next reconfigure. The function
// is intended to use by WithPinOpenDrain(), WithPinOpenSource() and WithPinPushPull().
func (d *digitalPinConfig) SetDrive(drive int) bool {
if d.drive == drive {
return false
}
d.drive = drive
return true
}
// SetDebounce sets the input pin with the given debounce period for next reconfigure. The function
// is intended to use by WithPinDebounce().
func (d *digitalPinConfig) SetDebounce(period time.Duration) bool {
if d.debouncePeriod == period {
return false
}
d.debouncePeriod = period
return true
}
// SetEventHandlerForEdge sets the input pin to edge detection to call the event handler on specified edge. The
// function is intended to use by WithPinEventOnFallingEdge(), WithPinEventOnRisingEdge() and WithPinEventOnBothEdges().
func (d *digitalPinConfig) SetEventHandlerForEdge(
handler func(int, time.Duration, string, uint32, uint32),
edge int,
) bool {
if d.edge == edge {
return false
}
d.edge = edge
d.edgeEventHandler = handler
return true
}
// SetPollForEdgeDetection use a discrete input polling method to detect edges. A poll interval of zero or smaller
// will deactivate this function. Please note: Using this feature is CPU consuming and less accurate than using cdev
// event handler (gpiod implementation) and should be done only if the former is not implemented or not working for
// the adaptor. E.g. sysfs driver in gobot has not implemented edge detection yet. The function is only useful
// together with SetEventHandlerForEdge() and its corresponding With*() functions.
// The function is intended to use by WithPinPollForEdgeDetection().
//
//nolint:nonamedreturns // useful here
func (d *digitalPinConfig) SetPollForEdgeDetection(
pollInterval time.Duration,
pollQuitChan chan struct{},
) (changed bool) {
if d.pollInterval == pollInterval {
return false
}
d.pollInterval = pollInterval
d.pollQuitChan = pollQuitChan
return true
}