-
-
Notifications
You must be signed in to change notification settings - Fork 1k
/
Copy pathbutton_driver.go
176 lines (147 loc) · 4.43 KB
/
button_driver.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package gpio
import (
"fmt"
"time"
"gobot.io/x/gobot/v2"
)
// buttonOptionApplier needs to be implemented by each configurable option type
type buttonOptionApplier interface {
apply(cfg *buttonConfiguration)
}
// buttonConfiguration contains all changeable attributes of the driver.
type buttonConfiguration struct {
readInterval time.Duration
defaultState int
}
// buttonReadIntervalOption is the type for applying another read interval to the configuration
type buttonReadIntervalOption time.Duration
// buttonDefaultStateOption is the type for applying another default state to the configuration
type buttonDefaultStateOption int
// ButtonDriver Represents a digital Button
type ButtonDriver struct {
*driver
buttonCfg *buttonConfiguration
gobot.Eventer
active bool
halt chan struct{}
}
// NewButtonDriver returns a driver for a button with a polling interval for changed state of 10 milliseconds,
// given a DigitalReader and pin.
//
// Supported options:
//
// "WithName"
// "WithButtonPollInterval"
func NewButtonDriver(a DigitalReader, pin string, opts ...interface{}) *ButtonDriver {
//nolint:forcetypeassert // no error return value, so there is no better way
d := &ButtonDriver{
driver: newDriver(a.(gobot.Connection), "Button", withPin(pin)),
buttonCfg: &buttonConfiguration{readInterval: 10 * time.Millisecond, defaultState: 0},
}
d.afterStart = d.initialize
d.beforeHalt = d.shutdown
for _, opt := range opts {
switch o := opt.(type) {
case optionApplier:
o.apply(d.driverCfg)
case buttonOptionApplier:
o.apply(d.buttonCfg)
case time.Duration:
// TODO this is only for backward compatibility and will be removed after version 2.x
d.buttonCfg.readInterval = o
default:
panic(fmt.Sprintf("'%s' can not be applied on '%s'", opt, d.driverCfg.name))
}
}
return d
}
// WithButtonPollInterval change the asynchronous cyclic reading interval from default 10ms to the given value.
func WithButtonPollInterval(interval time.Duration) buttonOptionApplier {
return buttonReadIntervalOption(interval)
}
// WithButtonDefaultState change the default state from default 0 to the given value.
func WithButtonDefaultState(s int) buttonOptionApplier {
return buttonDefaultStateOption(s)
}
// Active gets the current state
func (d *ButtonDriver) Active() bool {
// ensure that read and write can not interfere
d.mutex.Lock()
defer d.mutex.Unlock()
return d.active
}
// SetDefaultState for the next start.
// Deprecated: Please use option [gpio.WithButtonDefaultState] instead.
func (d *ButtonDriver) SetDefaultState(s int) {
// ensure that read and write can not interfere
d.mutex.Lock()
defer d.mutex.Unlock()
WithButtonDefaultState(s).apply(d.buttonCfg)
}
// initialize the ButtonDriver and polls the state of the button at the given interval.
//
// Emits the Events:
//
// Push int - On button push
// Release int - On button release
// Error error - On button error
func (d *ButtonDriver) initialize() error {
if d.buttonCfg.readInterval == 0 {
return fmt.Errorf("the read interval for button needs to be greater than zero")
}
d.Eventer = gobot.NewEventer()
d.AddEvent(ButtonPush)
d.AddEvent(ButtonRelease)
d.AddEvent(Error)
d.halt = make(chan struct{})
state := d.buttonCfg.defaultState
go func() {
for {
select {
case <-time.After(d.buttonCfg.readInterval):
newValue, err := d.digitalRead(d.driverCfg.pin)
if err != nil {
d.Publish(Error, err)
} else if newValue != state && newValue != -1 {
state = newValue
d.update(newValue)
}
case <-d.halt:
return
}
}
}()
return nil
}
func (d *ButtonDriver) shutdown() error {
if d.buttonCfg.readInterval == 0 || d.halt == nil {
// cyclic reading deactivated
return nil
}
close(d.halt) // broadcast halt, also to the test
return nil
}
func (d *ButtonDriver) update(newValue int) {
// ensure that read and write can not interfere
d.mutex.Lock()
defer d.mutex.Unlock()
if newValue != d.buttonCfg.defaultState {
d.active = true
d.Publish(ButtonPush, newValue)
} else {
d.active = false
d.Publish(ButtonRelease, newValue)
}
}
func (o buttonReadIntervalOption) String() string {
return "read interval option for buttons"
}
func (o buttonDefaultStateOption) String() string {
return "default state option for buttons"
}
func (o buttonReadIntervalOption) apply(cfg *buttonConfiguration) {
cfg.readInterval = time.Duration(o)
}
func (o buttonDefaultStateOption) apply(cfg *buttonConfiguration) {
cfg.defaultState = int(o)
}