mirror of
https://github.com/hybridgroup/gobot.git
synced 2025-04-26 13:48:49 +08:00
gpio: fix data race in ButtonDriver (#1027)
This commit is contained in:
parent
6ef7450e2e
commit
c41604f5f9
@ -284,7 +284,7 @@ a shared set of drivers provided using the `gobot/drivers/gpio` package:
|
|||||||
- HC-SR04 Ultrasonic Ranging Module
|
- HC-SR04 Ultrasonic Ranging Module
|
||||||
- HD44780 LCD controller
|
- HD44780 LCD controller
|
||||||
- LED
|
- LED
|
||||||
- Makey Button
|
- Makey Button (by using driver for Button)
|
||||||
- MAX7219 LED Dot Matrix
|
- MAX7219 LED Dot Matrix
|
||||||
- Motor
|
- Motor
|
||||||
- Proximity Infra Red (PIR) Motion Sensor
|
- Proximity Infra Red (PIR) Motion Sensor
|
||||||
|
@ -26,7 +26,7 @@ Gobot has a extensible system for connecting to hardware devices. The following
|
|||||||
- HC-SR04 Ultrasonic Ranging Module
|
- HC-SR04 Ultrasonic Ranging Module
|
||||||
- HD44780 LCD controller
|
- HD44780 LCD controller
|
||||||
- LED
|
- LED
|
||||||
- Makey Button
|
- Makey Button (by using driver for Button)
|
||||||
- MAX7219 LED Dot Matrix
|
- MAX7219 LED Dot Matrix
|
||||||
- Motor
|
- Motor
|
||||||
- Proximity Infra Red (PIR) Motion Sensor
|
- Proximity Infra Red (PIR) Motion Sensor
|
||||||
|
@ -8,14 +8,13 @@ import (
|
|||||||
|
|
||||||
// ButtonDriver Represents a digital Button
|
// ButtonDriver Represents a digital Button
|
||||||
type ButtonDriver struct {
|
type ButtonDriver struct {
|
||||||
Active bool
|
*Driver
|
||||||
DefaultState int
|
gobot.Eventer
|
||||||
pin string
|
pin string
|
||||||
name string
|
active bool
|
||||||
|
defaultState int
|
||||||
halt chan bool
|
halt chan bool
|
||||||
interval time.Duration
|
interval time.Duration
|
||||||
connection DigitalReader
|
|
||||||
gobot.Eventer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewButtonDriver returns a new ButtonDriver with a polling interval of
|
// NewButtonDriver returns a new ButtonDriver with a polling interval of
|
||||||
@ -26,15 +25,16 @@ type ButtonDriver struct {
|
|||||||
// time.Duration: Interval at which the ButtonDriver is polled for new information
|
// time.Duration: Interval at which the ButtonDriver is polled for new information
|
||||||
func NewButtonDriver(a DigitalReader, pin string, v ...time.Duration) *ButtonDriver {
|
func NewButtonDriver(a DigitalReader, pin string, v ...time.Duration) *ButtonDriver {
|
||||||
b := &ButtonDriver{
|
b := &ButtonDriver{
|
||||||
name: gobot.DefaultName("Button"),
|
Driver: NewDriver(a.(gobot.Connection), "Button"),
|
||||||
connection: a,
|
|
||||||
pin: pin,
|
|
||||||
Active: false,
|
|
||||||
DefaultState: 0,
|
|
||||||
Eventer: gobot.NewEventer(),
|
Eventer: gobot.NewEventer(),
|
||||||
|
pin: pin,
|
||||||
|
active: false,
|
||||||
|
defaultState: 0,
|
||||||
interval: 10 * time.Millisecond,
|
interval: 10 * time.Millisecond,
|
||||||
halt: make(chan bool),
|
halt: make(chan bool),
|
||||||
}
|
}
|
||||||
|
b.afterStart = b.initialize
|
||||||
|
b.beforeHalt = b.shutdown
|
||||||
|
|
||||||
if len(v) > 0 {
|
if len(v) > 0 {
|
||||||
b.interval = v[0]
|
b.interval = v[0]
|
||||||
@ -47,18 +47,39 @@ func NewButtonDriver(a DigitalReader, pin string, v ...time.Duration) *ButtonDri
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts the ButtonDriver and polls the state of the button at the given interval.
|
// Pin returns the ButtonDrivers pin
|
||||||
|
func (b *ButtonDriver) Pin() string { return b.pin }
|
||||||
|
|
||||||
|
// Active gets the current state
|
||||||
|
func (b *ButtonDriver) Active() bool {
|
||||||
|
// ensure that read and write can not interfere
|
||||||
|
b.mutex.Lock()
|
||||||
|
defer b.mutex.Unlock()
|
||||||
|
|
||||||
|
return b.active
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDefaultState for the next start.
|
||||||
|
func (b *ButtonDriver) SetDefaultState(s int) {
|
||||||
|
// ensure that read and write can not interfere
|
||||||
|
b.mutex.Lock()
|
||||||
|
defer b.mutex.Unlock()
|
||||||
|
|
||||||
|
b.defaultState = s
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize the ButtonDriver and polls the state of the button at the given interval.
|
||||||
//
|
//
|
||||||
// Emits the Events:
|
// Emits the Events:
|
||||||
//
|
//
|
||||||
// Push int - On button push
|
// Push int - On button push
|
||||||
// Release int - On button release
|
// Release int - On button release
|
||||||
// Error error - On button error
|
// Error error - On button error
|
||||||
func (b *ButtonDriver) Start() (err error) {
|
func (b *ButtonDriver) initialize() (err error) {
|
||||||
state := b.DefaultState
|
state := b.defaultState
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
newValue, err := b.connection.DigitalRead(b.Pin())
|
newValue, err := b.connection.(DigitalReader).DigitalRead(b.Pin())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Publish(Error, err)
|
b.Publish(Error, err)
|
||||||
} else if newValue != state && newValue != -1 {
|
} else if newValue != state && newValue != -1 {
|
||||||
@ -75,30 +96,21 @@ func (b *ButtonDriver) Start() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Halt stops polling the button for new information
|
func (b *ButtonDriver) shutdown() (err error) {
|
||||||
func (b *ButtonDriver) Halt() (err error) {
|
|
||||||
b.halt <- true
|
b.halt <- true
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name returns the ButtonDrivers name
|
|
||||||
func (b *ButtonDriver) Name() string { return b.name }
|
|
||||||
|
|
||||||
// SetName sets the ButtonDrivers name
|
|
||||||
func (b *ButtonDriver) SetName(n string) { b.name = n }
|
|
||||||
|
|
||||||
// Pin returns the ButtonDrivers pin
|
|
||||||
func (b *ButtonDriver) Pin() string { return b.pin }
|
|
||||||
|
|
||||||
// Connection returns the ButtonDrivers Connection
|
|
||||||
func (b *ButtonDriver) Connection() gobot.Connection { return b.connection.(gobot.Connection) }
|
|
||||||
|
|
||||||
func (b *ButtonDriver) update(newValue int) {
|
func (b *ButtonDriver) update(newValue int) {
|
||||||
if newValue != b.DefaultState {
|
// ensure that read and write can not interfere
|
||||||
b.Active = true
|
b.mutex.Lock()
|
||||||
|
defer b.mutex.Unlock()
|
||||||
|
|
||||||
|
if newValue != b.defaultState {
|
||||||
|
b.active = true
|
||||||
b.Publish(ButtonPush, newValue)
|
b.Publish(ButtonPush, newValue)
|
||||||
} else {
|
} else {
|
||||||
b.Active = false
|
b.active = false
|
||||||
b.Publish(ButtonRelease, newValue)
|
b.Publish(ButtonRelease, newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,12 @@ package gpio
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"gobot.io/x/gobot/v2"
|
"gobot.io/x/gobot/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,43 +16,66 @@ var _ gobot.Driver = (*ButtonDriver)(nil)
|
|||||||
|
|
||||||
const buttonTestDelay = 250
|
const buttonTestDelay = 250
|
||||||
|
|
||||||
func initTestButtonDriver() *ButtonDriver {
|
func initTestButtonDriverWithStubbedAdaptor() (*ButtonDriver, *gpioTestAdaptor) {
|
||||||
return NewButtonDriver(newGpioTestAdaptor(), "1")
|
a := newGpioTestAdaptor()
|
||||||
|
d := NewButtonDriver(a, "1")
|
||||||
|
return d, a
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestButtonDriverHalt(t *testing.T) {
|
func TestNewButtonDriver(t *testing.T) {
|
||||||
d := initTestButtonDriver()
|
// arrange
|
||||||
go func() {
|
a := newGpioTestAdaptor()
|
||||||
<-d.halt
|
// act
|
||||||
}()
|
d := NewButtonDriver(a, "1")
|
||||||
assert.NoError(t, d.Halt())
|
// assert
|
||||||
}
|
assert.IsType(t, &ButtonDriver{}, d)
|
||||||
|
assert.True(t, strings.HasPrefix(d.name, "Button"))
|
||||||
func TestButtonDriver(t *testing.T) {
|
assert.Equal(t, a, d.connection)
|
||||||
d := NewButtonDriver(newGpioTestAdaptor(), "1")
|
assert.NotNil(t, d.afterStart)
|
||||||
assert.NotNil(t, d.Connection())
|
assert.NotNil(t, d.beforeHalt)
|
||||||
|
assert.NotNil(t, d.Commander)
|
||||||
|
assert.NotNil(t, d.mutex)
|
||||||
|
assert.NotNil(t, d.Eventer)
|
||||||
|
assert.Equal(t, "1", d.pin)
|
||||||
|
assert.Equal(t, false, d.active)
|
||||||
|
assert.Equal(t, 0, d.defaultState)
|
||||||
|
assert.Equal(t, 10*time.Millisecond, d.interval)
|
||||||
|
assert.NotNil(t, d.halt)
|
||||||
|
// act & assert other interval
|
||||||
d = NewButtonDriver(newGpioTestAdaptor(), "1", 30*time.Second)
|
d = NewButtonDriver(newGpioTestAdaptor(), "1", 30*time.Second)
|
||||||
assert.Equal(t, 30*time.Second, d.interval)
|
assert.Equal(t, 30*time.Second, d.interval)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestButtonDriverStart(t *testing.T) {
|
func TestButtonStart(t *testing.T) {
|
||||||
|
// arrange
|
||||||
sem := make(chan bool)
|
sem := make(chan bool)
|
||||||
a := newGpioTestAdaptor()
|
nextVal := make(chan int, 1)
|
||||||
d := NewButtonDriver(a, "1")
|
d, a := initTestButtonDriverWithStubbedAdaptor()
|
||||||
|
|
||||||
|
a.digitalReadFunc = func(string) (int, error) {
|
||||||
|
val := 1
|
||||||
|
var err error
|
||||||
|
select {
|
||||||
|
case val = <-nextVal:
|
||||||
|
if val < 0 {
|
||||||
|
err = errors.New("digital read error")
|
||||||
|
}
|
||||||
|
return val, err
|
||||||
|
default:
|
||||||
|
return val, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_ = d.Once(ButtonPush, func(data interface{}) {
|
_ = d.Once(ButtonPush, func(data interface{}) {
|
||||||
assert.True(t, d.Active)
|
assert.True(t, d.Active())
|
||||||
|
nextVal <- 0
|
||||||
sem <- true
|
sem <- true
|
||||||
})
|
})
|
||||||
|
|
||||||
a.digitalReadFunc = func(string) (val int, err error) {
|
// act
|
||||||
val = 1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t, d.Start())
|
assert.NoError(t, d.Start())
|
||||||
|
|
||||||
|
// assert & rearrange
|
||||||
select {
|
select {
|
||||||
case <-sem:
|
case <-sem:
|
||||||
case <-time.After(buttonTestDelay * time.Millisecond):
|
case <-time.After(buttonTestDelay * time.Millisecond):
|
||||||
@ -58,15 +83,11 @@ func TestButtonDriverStart(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ = d.Once(ButtonRelease, func(data interface{}) {
|
_ = d.Once(ButtonRelease, func(data interface{}) {
|
||||||
assert.False(t, d.Active)
|
assert.False(t, d.Active())
|
||||||
|
nextVal <- -1
|
||||||
sem <- true
|
sem <- true
|
||||||
})
|
})
|
||||||
|
|
||||||
a.digitalReadFunc = func(string) (val int, err error) {
|
|
||||||
val = 0
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-sem:
|
case <-sem:
|
||||||
case <-time.After(buttonTestDelay * time.Millisecond):
|
case <-time.After(buttonTestDelay * time.Millisecond):
|
||||||
@ -77,11 +98,6 @@ func TestButtonDriverStart(t *testing.T) {
|
|||||||
sem <- true
|
sem <- true
|
||||||
})
|
})
|
||||||
|
|
||||||
a.digitalReadFunc = func(string) (val int, err error) {
|
|
||||||
err = errors.New("digital read error")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-sem:
|
case <-sem:
|
||||||
case <-time.After(buttonTestDelay * time.Millisecond):
|
case <-time.After(buttonTestDelay * time.Millisecond):
|
||||||
@ -93,11 +109,7 @@ func TestButtonDriverStart(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
d.halt <- true
|
d.halt <- true
|
||||||
|
nextVal <- 1
|
||||||
a.digitalReadFunc = func(string) (val int, err error) {
|
|
||||||
val = 1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-sem:
|
case <-sem:
|
||||||
@ -106,22 +118,32 @@ func TestButtonDriverStart(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestButtonDriverDefaultState(t *testing.T) {
|
func TestButtonSetDefaultState(t *testing.T) {
|
||||||
|
// arrange
|
||||||
sem := make(chan bool)
|
sem := make(chan bool)
|
||||||
a := newGpioTestAdaptor()
|
nextVal := make(chan int, 1)
|
||||||
d := NewButtonDriver(a, "1")
|
d, a := initTestButtonDriverWithStubbedAdaptor()
|
||||||
d.DefaultState = 1
|
|
||||||
|
|
||||||
|
a.digitalReadFunc = func(string) (int, error) {
|
||||||
|
val := 0
|
||||||
|
select {
|
||||||
|
case val = <-nextVal:
|
||||||
|
return val, nil
|
||||||
|
default:
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
_ = d.Once(ButtonPush, func(data interface{}) {
|
_ = d.Once(ButtonPush, func(data interface{}) {
|
||||||
assert.True(t, d.Active)
|
assert.True(t, d.Active())
|
||||||
|
nextVal <- 1
|
||||||
sem <- true
|
sem <- true
|
||||||
})
|
})
|
||||||
|
|
||||||
a.digitalReadFunc = func(string) (val int, err error) {
|
// act
|
||||||
val = 0
|
d.SetDefaultState(1)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// assert & rearrange
|
||||||
|
require.Equal(t, 1, d.defaultState)
|
||||||
assert.NoError(t, d.Start())
|
assert.NoError(t, d.Start())
|
||||||
|
|
||||||
select {
|
select {
|
||||||
@ -131,15 +153,11 @@ func TestButtonDriverDefaultState(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ = d.Once(ButtonRelease, func(data interface{}) {
|
_ = d.Once(ButtonRelease, func(data interface{}) {
|
||||||
assert.False(t, d.Active)
|
assert.False(t, d.Active())
|
||||||
|
|
||||||
sem <- true
|
sem <- true
|
||||||
})
|
})
|
||||||
|
|
||||||
a.digitalReadFunc = func(string) (val int, err error) {
|
|
||||||
val = 1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-sem:
|
case <-sem:
|
||||||
case <-time.After(buttonTestDelay * time.Millisecond):
|
case <-time.After(buttonTestDelay * time.Millisecond):
|
||||||
@ -147,13 +165,56 @@ func TestButtonDriverDefaultState(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestButtonDriverDefaultName(t *testing.T) {
|
func TestButtonHalt(t *testing.T) {
|
||||||
g := initTestButtonDriver()
|
// arrange
|
||||||
assert.True(t, strings.HasPrefix(g.Name(), "Button"))
|
d, _ := initTestButtonDriverWithStubbedAdaptor()
|
||||||
|
const timeout = 10 * time.Microsecond
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
select {
|
||||||
|
case <-d.halt: // wait until halt was set to the channel
|
||||||
|
case <-time.After(timeout): // otherwise run into the timeout
|
||||||
|
t.Errorf("halt was not received within %s", timeout)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// act & assert
|
||||||
|
assert.NoError(t, d.Halt())
|
||||||
|
wg.Wait() // wait until the go function was really finished
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestButtonDriverSetName(t *testing.T) {
|
func TestButtonPin(t *testing.T) {
|
||||||
g := initTestButtonDriver()
|
tests := map[string]struct {
|
||||||
g.SetName("mybot")
|
want string
|
||||||
assert.Equal(t, "mybot", g.Name())
|
}{
|
||||||
|
"10": {want: "10"},
|
||||||
|
"36": {want: "36"},
|
||||||
|
}
|
||||||
|
for name, tc := range tests {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
// arrange
|
||||||
|
d := ButtonDriver{pin: name}
|
||||||
|
// act & assert
|
||||||
|
assert.Equal(t, tc.want, d.Pin())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestButtonActive(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
"active_true": {want: true},
|
||||||
|
"active_false": {want: false},
|
||||||
|
}
|
||||||
|
for name, tc := range tests {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
// arrange
|
||||||
|
d := ButtonDriver{Driver: NewDriver(nil, "Button")} // just for mutex
|
||||||
|
d.active = tc.want
|
||||||
|
// act & assert
|
||||||
|
assert.Equal(t, tc.want, d.Active())
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,101 +0,0 @@
|
|||||||
package gpio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"gobot.io/x/gobot/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MakeyButtonDriver Represents a Makey Button
|
|
||||||
type MakeyButtonDriver struct {
|
|
||||||
name string
|
|
||||||
pin string
|
|
||||||
halt chan bool
|
|
||||||
connection DigitalReader
|
|
||||||
Active bool
|
|
||||||
interval time.Duration
|
|
||||||
gobot.Eventer
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMakeyButtonDriver returns a new MakeyButtonDriver with a polling interval of
|
|
||||||
// 10 Milliseconds given a DigitalReader and pin.
|
|
||||||
//
|
|
||||||
// Optionally accepts:
|
|
||||||
//
|
|
||||||
// time.Duration: Interval at which the ButtonDriver is polled for new information
|
|
||||||
func NewMakeyButtonDriver(a DigitalReader, pin string, v ...time.Duration) *MakeyButtonDriver {
|
|
||||||
m := &MakeyButtonDriver{
|
|
||||||
name: gobot.DefaultName("MakeyButton"),
|
|
||||||
connection: a,
|
|
||||||
pin: pin,
|
|
||||||
Active: false,
|
|
||||||
Eventer: gobot.NewEventer(),
|
|
||||||
interval: 10 * time.Millisecond,
|
|
||||||
halt: make(chan bool),
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(v) > 0 {
|
|
||||||
m.interval = v[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
m.AddEvent(Error)
|
|
||||||
m.AddEvent(ButtonPush)
|
|
||||||
m.AddEvent(ButtonRelease)
|
|
||||||
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the MakeyButtonDrivers name
|
|
||||||
func (b *MakeyButtonDriver) Name() string { return b.name }
|
|
||||||
|
|
||||||
// SetName sets the MakeyButtonDrivers name
|
|
||||||
func (b *MakeyButtonDriver) SetName(n string) { b.name = n }
|
|
||||||
|
|
||||||
// Pin returns the MakeyButtonDrivers pin
|
|
||||||
func (b *MakeyButtonDriver) Pin() string { return b.pin }
|
|
||||||
|
|
||||||
// Connection returns the MakeyButtonDrivers Connection
|
|
||||||
func (b *MakeyButtonDriver) Connection() gobot.Connection { return b.connection.(gobot.Connection) }
|
|
||||||
|
|
||||||
// Start starts the MakeyButtonDriver 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 (b *MakeyButtonDriver) Start() (err error) {
|
|
||||||
state := 1
|
|
||||||
go func() {
|
|
||||||
timer := time.NewTimer(b.interval)
|
|
||||||
timer.Stop()
|
|
||||||
for {
|
|
||||||
newValue, err := b.connection.DigitalRead(b.Pin())
|
|
||||||
if err != nil {
|
|
||||||
b.Publish(Error, err)
|
|
||||||
} else if newValue != state && newValue != -1 {
|
|
||||||
state = newValue
|
|
||||||
if newValue == 0 {
|
|
||||||
b.Active = true
|
|
||||||
b.Publish(ButtonPush, newValue)
|
|
||||||
} else {
|
|
||||||
b.Active = false
|
|
||||||
b.Publish(ButtonRelease, newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
timer.Reset(b.interval)
|
|
||||||
select {
|
|
||||||
case <-timer.C:
|
|
||||||
case <-b.halt:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Halt stops polling the makey button for new information
|
|
||||||
func (b *MakeyButtonDriver) Halt() (err error) {
|
|
||||||
b.halt <- true
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,117 +0,0 @@
|
|||||||
package gpio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"gobot.io/x/gobot/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ gobot.Driver = (*MakeyButtonDriver)(nil)
|
|
||||||
|
|
||||||
const makeyTestDelay = 250
|
|
||||||
|
|
||||||
func initTestMakeyButtonDriver() *MakeyButtonDriver {
|
|
||||||
return NewMakeyButtonDriver(newGpioTestAdaptor(), "1")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMakeyButtonDriverHalt(t *testing.T) {
|
|
||||||
d := initTestMakeyButtonDriver()
|
|
||||||
done := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
<-d.halt
|
|
||||||
close(done)
|
|
||||||
}()
|
|
||||||
assert.NoError(t, d.Halt())
|
|
||||||
select {
|
|
||||||
case <-done:
|
|
||||||
case <-time.After(makeyTestDelay * time.Millisecond):
|
|
||||||
t.Errorf("MakeyButton was not halted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMakeyButtonDriver(t *testing.T) {
|
|
||||||
d := initTestMakeyButtonDriver()
|
|
||||||
assert.Equal(t, "1", d.Pin())
|
|
||||||
assert.NotNil(t, d.Connection())
|
|
||||||
assert.Equal(t, 10*time.Millisecond, d.interval)
|
|
||||||
|
|
||||||
d = NewMakeyButtonDriver(newGpioTestAdaptor(), "1", 30*time.Second)
|
|
||||||
assert.Equal(t, 30*time.Second, d.interval)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMakeyButtonDriverStart(t *testing.T) {
|
|
||||||
sem := make(chan bool)
|
|
||||||
a := newGpioTestAdaptor()
|
|
||||||
d := NewMakeyButtonDriver(a, "1")
|
|
||||||
|
|
||||||
assert.NoError(t, d.Start())
|
|
||||||
|
|
||||||
_ = d.Once(ButtonPush, func(data interface{}) {
|
|
||||||
assert.True(t, d.Active)
|
|
||||||
sem <- true
|
|
||||||
})
|
|
||||||
|
|
||||||
a.digitalReadFunc = func(string) (val int, err error) {
|
|
||||||
val = 0
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-sem:
|
|
||||||
case <-time.After(makeyTestDelay * time.Millisecond):
|
|
||||||
t.Errorf("MakeyButton Event \"Push\" was not published")
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = d.Once(ButtonRelease, func(data interface{}) {
|
|
||||||
assert.False(t, d.Active)
|
|
||||||
sem <- true
|
|
||||||
})
|
|
||||||
|
|
||||||
a.digitalReadFunc = func(string) (val int, err error) {
|
|
||||||
val = 1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-sem:
|
|
||||||
case <-time.After(makeyTestDelay * time.Millisecond):
|
|
||||||
t.Errorf("MakeyButton Event \"Release\" was not published")
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = d.Once(Error, func(data interface{}) {
|
|
||||||
assert.Equal(t, "digital read error", data.(error).Error())
|
|
||||||
sem <- true
|
|
||||||
})
|
|
||||||
|
|
||||||
a.digitalReadFunc = func(string) (val int, err error) {
|
|
||||||
err = errors.New("digital read error")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-sem:
|
|
||||||
case <-time.After(makeyTestDelay * time.Millisecond):
|
|
||||||
t.Errorf("MakeyButton Event \"Error\" was not published")
|
|
||||||
}
|
|
||||||
|
|
||||||
// send a halt message
|
|
||||||
_ = d.Once(ButtonRelease, func(data interface{}) {
|
|
||||||
sem <- true
|
|
||||||
})
|
|
||||||
|
|
||||||
a.digitalReadFunc = func(string) (val int, err error) {
|
|
||||||
val = 1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
d.halt <- true
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-sem:
|
|
||||||
t.Errorf("MakeyButton Event should not have been published")
|
|
||||||
case <-time.After(makeyTestDelay * time.Millisecond):
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
//go:build example
|
|
||||||
// +build example
|
|
||||||
|
|
||||||
//
|
|
||||||
// Do not build by default.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"gobot.io/x/gobot/v2"
|
|
||||||
"gobot.io/x/gobot/v2/drivers/gpio"
|
|
||||||
"gobot.io/x/gobot/v2/platforms/beaglebone"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
beagleboneAdaptor := beaglebone.NewAdaptor()
|
|
||||||
button := gpio.NewMakeyButtonDriver(beagleboneAdaptor, "P8_09")
|
|
||||||
|
|
||||||
work := func() {
|
|
||||||
button.On(gpio.ButtonPush, func(data interface{}) {
|
|
||||||
fmt.Println("button pressed")
|
|
||||||
})
|
|
||||||
|
|
||||||
button.On(gpio.ButtonRelease, func(data interface{}) {
|
|
||||||
fmt.Println("button released")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
robot := gobot.NewRobot("makeyBot",
|
|
||||||
[]gobot.Connection{beagleboneAdaptor},
|
|
||||||
[]gobot.Device{button},
|
|
||||||
work,
|
|
||||||
)
|
|
||||||
|
|
||||||
robot.Start()
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
//go:build example
|
|
||||||
// +build example
|
|
||||||
|
|
||||||
//
|
|
||||||
// Do not build by default.
|
|
||||||
|
|
||||||
/*
|
|
||||||
How to run
|
|
||||||
Pass serial port to use as the first param:
|
|
||||||
|
|
||||||
go run examples/firmata_makey_button.go /dev/ttyACM0
|
|
||||||
*/
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"gobot.io/x/gobot/v2"
|
|
||||||
"gobot.io/x/gobot/v2/drivers/gpio"
|
|
||||||
"gobot.io/x/gobot/v2/platforms/firmata"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
firmataAdaptor := firmata.NewAdaptor(os.Args[1])
|
|
||||||
button := gpio.NewMakeyButtonDriver(firmataAdaptor, "2")
|
|
||||||
led := gpio.NewLedDriver(firmataAdaptor, "13")
|
|
||||||
|
|
||||||
work := func() {
|
|
||||||
button.On(gpio.ButtonPush, func(data interface{}) {
|
|
||||||
led.On()
|
|
||||||
})
|
|
||||||
|
|
||||||
button.On(gpio.ButtonRelease, func(data interface{}) {
|
|
||||||
led.Off()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
robot := gobot.NewRobot("makeyBot",
|
|
||||||
[]gobot.Connection{firmataAdaptor},
|
|
||||||
[]gobot.Device{button, led},
|
|
||||||
work,
|
|
||||||
)
|
|
||||||
|
|
||||||
robot.Start()
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user