2022-11-27 23:06:09 +08:00
|
|
|
package system
|
|
|
|
|
|
|
|
import (
|
2023-01-28 19:22:32 +08:00
|
|
|
"time"
|
|
|
|
|
2023-05-20 20:25:21 +08:00
|
|
|
"gobot.io/x/gobot/v2"
|
2022-11-27 23:06:09 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// IN gpio direction
|
|
|
|
IN = "in"
|
|
|
|
// OUT gpio direction
|
|
|
|
OUT = "out"
|
|
|
|
// HIGH gpio level
|
|
|
|
HIGH = 1
|
|
|
|
// LOW gpio level
|
|
|
|
LOW = 0
|
|
|
|
)
|
|
|
|
|
2023-01-28 19:22:32 +08:00
|
|
|
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"
|
|
|
|
)
|
|
|
|
|
2022-11-27 23:06:09 +08:00
|
|
|
type digitalPinConfig struct {
|
2023-01-28 19:22:32 +08:00
|
|
|
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)
|
2023-10-27 02:41:41 +08:00
|
|
|
pollInterval time.Duration
|
|
|
|
pollQuitChan chan struct{}
|
2022-11-27 23:06:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func newDigitalPinConfig(label string, options ...func(gobot.DigitalPinOptioner) bool) *digitalPinConfig {
|
|
|
|
cfg := &digitalPinConfig{
|
|
|
|
label: label,
|
|
|
|
direction: IN,
|
|
|
|
}
|
|
|
|
for _, option := range options {
|
|
|
|
option(cfg)
|
|
|
|
}
|
|
|
|
return cfg
|
|
|
|
}
|
|
|
|
|
2023-01-28 19:22:32 +08:00
|
|
|
// WithPinLabel use a pin label, which will replace the default label "gobotio#".
|
|
|
|
func WithPinLabel(label string) func(gobot.DigitalPinOptioner) bool {
|
2022-11-27 23:06:09 +08:00
|
|
|
return func(d gobot.DigitalPinOptioner) bool { return d.SetLabel(label) }
|
|
|
|
}
|
|
|
|
|
2023-01-28 19:22:32 +08:00
|
|
|
// WithPinDirectionOutput initializes the pin as output instead of the default "input".
|
|
|
|
func WithPinDirectionOutput(initial int) func(gobot.DigitalPinOptioner) bool {
|
2022-11-27 23:06:09 +08:00
|
|
|
return func(d gobot.DigitalPinOptioner) bool { return d.SetDirectionOutput(initial) }
|
|
|
|
}
|
|
|
|
|
2023-01-28 19:22:32 +08:00
|
|
|
// WithPinDirectionInput initializes the pin as input.
|
|
|
|
func WithPinDirectionInput() func(gobot.DigitalPinOptioner) bool {
|
2022-11-27 23:06:09 +08:00
|
|
|
return func(d gobot.DigitalPinOptioner) bool { return d.SetDirectionInput() }
|
|
|
|
}
|
|
|
|
|
2023-01-28 19:22:32 +08:00
|
|
|
// 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,
|
2023-10-21 02:50:42 +08:00
|
|
|
lseqno uint32),
|
|
|
|
) func(gobot.DigitalPinOptioner) bool {
|
2023-01-28 19:22:32 +08:00
|
|
|
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,
|
2023-10-21 02:50:42 +08:00
|
|
|
lseqno uint32),
|
|
|
|
) func(gobot.DigitalPinOptioner) bool {
|
2023-01-28 19:22:32 +08:00
|
|
|
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,
|
2023-10-21 02:50:42 +08:00
|
|
|
lseqno uint32),
|
|
|
|
) func(gobot.DigitalPinOptioner) bool {
|
2023-01-28 19:22:32 +08:00
|
|
|
return func(d gobot.DigitalPinOptioner) bool {
|
|
|
|
return d.SetEventHandlerForEdge(handler, digitalPinEventOnBothEdges)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-27 02:41:41 +08:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-28 19:22:32 +08:00
|
|
|
// SetLabel sets the label to use for next reconfigure. The function is intended to use by WithPinLabel().
|
2022-11-27 23:06:09 +08:00
|
|
|
func (d *digitalPinConfig) SetLabel(label string) bool {
|
|
|
|
if d.label == label {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
d.label = label
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2023-01-28 19:22:32 +08:00
|
|
|
// SetDirectionOutput sets the direction to output for next reconfigure. The function is intended to use
|
|
|
|
// by WithPinDirectionOutput().
|
2022-11-27 23:06:09 +08:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-01-28 19:22:32 +08:00
|
|
|
// SetDirectionInput sets the direction to input for next reconfigure. The function is intended to use
|
|
|
|
// by WithPinDirectionInput().
|
2022-11-27 23:06:09 +08:00
|
|
|
func (d *digitalPinConfig) SetDirectionInput() bool {
|
|
|
|
if d.direction == IN {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
d.direction = IN
|
|
|
|
return true
|
|
|
|
}
|
2023-01-28 19:22:32 +08:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2023-10-27 02:41:41 +08:00
|
|
|
// SetEventHandlerForEdge sets the input pin to edge detection to call the event handler on specified edge. The
|
2023-01-28 19:22:32 +08:00
|
|
|
// function is intended to use by WithPinEventOnFallingEdge(), WithPinEventOnRisingEdge() and WithPinEventOnBothEdges().
|
2023-10-27 02:41:41 +08:00
|
|
|
func (d *digitalPinConfig) SetEventHandlerForEdge(
|
|
|
|
handler func(int, time.Duration, string, uint32, uint32),
|
|
|
|
edge int,
|
|
|
|
) bool {
|
2023-01-28 19:22:32 +08:00
|
|
|
if d.edge == edge {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
d.edge = edge
|
|
|
|
d.edgeEventHandler = handler
|
|
|
|
return true
|
|
|
|
}
|
2023-10-27 02:41:41 +08:00
|
|
|
|
|
|
|
// 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().
|
2023-11-16 03:51:52 +08:00
|
|
|
//
|
|
|
|
//nolint:nonamedreturns // useful here
|
2023-10-27 02:41:41 +08:00
|
|
|
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
|
|
|
|
}
|