Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gpio: fix data race in ButtonDriver #1027

Merged
merged 1 commit into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ a shared set of drivers provided using the `gobot/drivers/gpio` package:
- HC-SR04 Ultrasonic Ranging Module
- HD44780 LCD controller
- LED
- Makey Button
- Makey Button (by using driver for Button)
- MAX7219 LED Dot Matrix
- Motor
- Proximity Infra Red (PIR) Motion Sensor
Expand Down
2 changes: 1 addition & 1 deletion drivers/gpio/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Gobot has a extensible system for connecting to hardware devices. The following
- HC-SR04 Ultrasonic Ranging Module
- HD44780 LCD controller
- LED
- Makey Button
- Makey Button (by using driver for Button)
- MAX7219 LED Dot Matrix
- Motor
- Proximity Infra Red (PIR) Motion Sensor
Expand Down
76 changes: 44 additions & 32 deletions drivers/gpio/button_driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ import (

// ButtonDriver Represents a digital Button
type ButtonDriver struct {
Active bool
DefaultState int
*Driver
gobot.Eventer
pin string
name string
active bool
defaultState int
halt chan bool
interval time.Duration
connection DigitalReader
gobot.Eventer
}

// NewButtonDriver returns a new ButtonDriver with a polling interval of
Expand All @@ -26,15 +25,16 @@ type ButtonDriver struct {
// time.Duration: Interval at which the ButtonDriver is polled for new information
func NewButtonDriver(a DigitalReader, pin string, v ...time.Duration) *ButtonDriver {
b := &ButtonDriver{
name: gobot.DefaultName("Button"),
connection: a,
pin: pin,
Active: false,
DefaultState: 0,
Driver: NewDriver(a.(gobot.Connection), "Button"),
Eventer: gobot.NewEventer(),
pin: pin,
active: false,
defaultState: 0,
interval: 10 * time.Millisecond,
halt: make(chan bool),
}
b.afterStart = b.initialize
b.beforeHalt = b.shutdown

if len(v) > 0 {
b.interval = v[0]
Expand All @@ -47,18 +47,39 @@ func NewButtonDriver(a DigitalReader, pin string, v ...time.Duration) *ButtonDri
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:
//
// Push int - On button push
// Release int - On button release
// Error error - On button error
func (b *ButtonDriver) Start() (err error) {
state := b.DefaultState
func (b *ButtonDriver) initialize() (err error) {
state := b.defaultState
go func() {
for {
newValue, err := b.connection.DigitalRead(b.Pin())
newValue, err := b.connection.(DigitalReader).DigitalRead(b.Pin())
if err != nil {
b.Publish(Error, err)
} else if newValue != state && newValue != -1 {
Expand All @@ -75,30 +96,21 @@ func (b *ButtonDriver) Start() (err error) {
return
}

// Halt stops polling the button for new information
func (b *ButtonDriver) Halt() (err error) {
func (b *ButtonDriver) shutdown() (err error) {
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) {
if newValue != b.DefaultState {
b.Active = true
// ensure that read and write can not interfere
b.mutex.Lock()
defer b.mutex.Unlock()

if newValue != b.defaultState {
b.active = true
b.Publish(ButtonPush, newValue)
} else {
b.Active = false
b.active = false
b.Publish(ButtonRelease, newValue)
}
}
Loading