core: some WIP on using digitalread and analogread event subscriptions for all gpio drivers

Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
deadprogram 2016-12-20 12:06:45 +01:00
parent 4577cdad60
commit 8851344da1
6 changed files with 140 additions and 31 deletions

View File

@ -12,7 +12,7 @@ type AnalogSensorDriver struct {
pin string
halt chan bool
interval time.Duration
connection AnalogReader
connection AnalogReadEventer
gobot.Eventer
gobot.Commander
}
@ -25,7 +25,7 @@ type AnalogSensorDriver struct {
//
// Adds the following API Commands:
// "Read" - See AnalogSensor.Read
func NewAnalogSensorDriver(a AnalogReader, pin string, v ...time.Duration) *AnalogSensorDriver {
func NewAnalogSensorDriver(a AnalogReadEventer, pin string, v ...time.Duration) *AnalogSensorDriver {
d := &AnalogSensorDriver{
name: "AnalogSensor",
connection: a,
@ -56,25 +56,18 @@ func NewAnalogSensorDriver(a AnalogReader, pin string, v ...time.Duration) *Anal
// Data int - Event is emitted on change and represents the current reading from the sensor.
// Error error - Event is emitted on error reading from the sensor.
func (a *AnalogSensorDriver) Start() (err error) {
value := 0
go func() {
timer := time.NewTimer(a.interval)
timer.Stop()
for {
newValue, err := a.Read()
if err != nil {
a.Publish(a.Event(Error), err)
} else if newValue != value && newValue != -1 {
value = newValue
a.Publish(a.Event(Data), value)
}
evts := a.connection.SubscribeAnalogRead(a.Pin())
timer.Reset(a.interval)
go func() {
analogread := "analogread-" + a.Pin()
for {
select {
case <-timer.C:
case <-a.halt:
timer.Stop()
return
case evt := <-evts:
if evt.Name == analogread {
a.Publish(Data, evt.Data)
}
}
}
}()

View File

@ -73,3 +73,17 @@ type DigitalReader interface {
gobot.Adaptor
DigitalRead(string) (val int, err error)
}
// DigitalReadEventer interface represents an Adaptor which has SubscribeDigitalRead capabilities
type DigitalReadEventer interface {
gobot.Eventer
DigitalReader
SubscribeDigitalRead(string) (gobot.EventChannel)
}
// AnalogReadEventer interface represents an Adaptor which has SubscribeAnalogRead capabilities
type AnalogReadEventer interface {
gobot.Eventer
AnalogReader
SubscribeAnalogRead(string) (gobot.EventChannel)
}

View File

@ -34,7 +34,7 @@ type GroveRotaryDriver struct {
//
// Adds the following API Commands:
// "Read" - See AnalogSensor.Read
func NewGroveRotaryDriver(a AnalogReader, pin string, v ...time.Duration) *GroveRotaryDriver {
func NewGroveRotaryDriver(a AnalogReadEventer, pin string, v ...time.Duration) *GroveRotaryDriver {
return &GroveRotaryDriver{
AnalogSensorDriver: NewAnalogSensorDriver(a, pin, v...),
}
@ -72,7 +72,7 @@ type GroveLightSensorDriver struct {
//
// Adds the following API Commands:
// "Read" - See AnalogSensor.Read
func NewGroveLightSensorDriver(a AnalogReader, pin string, v ...time.Duration) *GroveLightSensorDriver {
func NewGroveLightSensorDriver(a AnalogReadEventer, pin string, v ...time.Duration) *GroveLightSensorDriver {
return &GroveLightSensorDriver{
AnalogSensorDriver: NewAnalogSensorDriver(a, pin, v...),
}
@ -92,7 +92,7 @@ type GrovePiezoVibrationSensorDriver struct {
//
// Adds the following API Commands:
// "Read" - See AnalogSensor.Read
func NewGrovePiezoVibrationSensorDriver(a AnalogReader, pin string, v ...time.Duration) *GrovePiezoVibrationSensorDriver {
func NewGrovePiezoVibrationSensorDriver(a AnalogReadEventer, pin string, v ...time.Duration) *GrovePiezoVibrationSensorDriver {
sensor := &GrovePiezoVibrationSensorDriver{
AnalogSensorDriver: NewAnalogSensorDriver(a, pin, v...),
}
@ -152,7 +152,7 @@ type GroveSoundSensorDriver struct {
//
// Adds the following API Commands:
// "Read" - See AnalogSensor.Read
func NewGroveSoundSensorDriver(a AnalogReader, pin string, v ...time.Duration) *GroveSoundSensorDriver {
func NewGroveSoundSensorDriver(a AnalogReadEventer, pin string, v ...time.Duration) *GroveSoundSensorDriver {
return &GroveSoundSensorDriver{
AnalogSensorDriver: NewAnalogSensorDriver(a, pin, v...),
}

View File

@ -2,17 +2,17 @@ package gobot
import "sync"
type eventChannel chan *Event
type EventChannel chan *Event
type eventer struct {
// map of valid Event names
eventnames map[string]string
// new events get put in to the event channel
in eventChannel
in EventChannel
// map of out channels used by subscribers
outs map[eventChannel]eventChannel
outs map[EventChannel]EventChannel
// mutex to protect the eventChannel map
eventsMutex sync.Mutex
@ -38,10 +38,10 @@ type Eventer interface {
Publish(name string, data interface{})
// Subscribe to events
Subscribe() (events eventChannel)
Subscribe() (events EventChannel)
// Unsubscribe from an event channel
Unsubscribe(events eventChannel)
Unsubscribe(events EventChannel)
// Event handler
On(name string, f func(s interface{})) (err error)
@ -54,8 +54,8 @@ type Eventer interface {
func NewEventer() Eventer {
evtr := &eventer{
eventnames: make(map[string]string),
in: make(eventChannel, 1),
outs: make(map[eventChannel]eventChannel),
in: make(EventChannel, 1),
outs: make(map[EventChannel]EventChannel),
}
// goroutine to cascade "in" events to all "out" event channels
@ -103,16 +103,16 @@ func (e *eventer) Publish(name string, data interface{}) {
}
// Subscribe to any events from this eventer
func (e *eventer) Subscribe() eventChannel {
func (e *eventer) Subscribe() EventChannel {
e.eventsMutex.Lock()
defer e.eventsMutex.Unlock()
out := make(eventChannel)
out := make(EventChannel)
e.outs[out] = out
return out
}
// Unsubscribe from the event channel
func (e *eventer) Unsubscribe(events eventChannel) {
func (e *eventer) Unsubscribe(events EventChannel) {
e.eventsMutex.Lock()
defer e.eventsMutex.Unlock()
delete(e.outs, events)

View File

@ -0,0 +1,33 @@
package main
import (
"fmt"
"gobot.io/x/gobot"
"gobot.io/x/gobot/drivers/gpio"
"gobot.io/x/gobot/platforms/beaglebone"
)
func main() {
a := beaglebone.NewAdaptor()
sensor := gpio.NewAnalogSensorDriver(a, "P9_39")
work := func() {
sensor.On(gpio.Data, func(data interface{}) {
voltage := (float64(data.(int)) * 1.8) / 1024 // BBB uses 1.8V
tempC := (voltage - 0.5) * 100
tempF := (tempC * 9 / 5) + 32
fmt.Printf("%.2f°C\n", tempC)
fmt.Printf("%.2f°F\n", tempF)
})
}
robot := gobot.NewRobot("sensorBot",
[]gobot.Connection{a},
[]gobot.Device{sensor},
work,
)
robot.Start()
}

View File

@ -9,10 +9,12 @@ import (
"path/filepath"
"strconv"
"strings"
"time"
multierror "github.com/hashicorp/go-multierror"
"gobot.io/x/gobot"
"gobot.io/x/gobot/sysfs"
"gobot.io/x/gobot/drivers/gpio"
)
var glob = func(pattern string) (matches []string, err error) {
@ -31,6 +33,9 @@ type Adaptor struct {
analogPath string
analogPinMap map[string]string
slots string
interval time.Duration
halt chan bool
gobot.Eventer
}
// NewAdaptor returns a new Beaglebone Adaptor
@ -39,6 +44,8 @@ func NewAdaptor() *Adaptor {
name: "Beaglebone",
digitalPins: make([]sysfs.DigitalPin, 120),
pwmPins: make(map[string]*pwmPin),
interval: 10 * time.Millisecond,
halt: make(chan bool),
}
b.setSlots()
@ -195,6 +202,68 @@ func (b *Adaptor) AnalogRead(pin string) (val int, err error) {
return
}
// SubscribeDigitalRead starts reading from the specified pin,
// and publishes events on the returned event channel when changes occur
func (b *Adaptor) SubscribeDigitalRead(pin string) (gobot.EventChannel) {
// TODO: replace with epoll based implementation
b.AddEvent("digitalread-" + pin)
value := 0
go func() {
timer := time.NewTimer(b.interval)
timer.Stop()
for {
newValue, err := b.DigitalRead(pin)
if err != nil {
b.Publish(b.Event(gpio.Error), err)
} else if newValue != value && newValue != -1 {
value = newValue
b.Publish(b.Event("digitalread-" + pin), value)
}
timer.Reset(b.interval)
select {
case <-timer.C:
case <-b.halt:
timer.Stop()
return
}
}
}()
return b.Subscribe()
}
// SubscribeAnalogRead starts reading from the specified pin,
// and publishes events on the returned event channel when changes occur
func (b *Adaptor) SubscribeAnalogRead(pin string) (gobot.EventChannel) {
// TODO: replace with epoll based implementation
b.AddEvent("analogread-" + pin)
value := 0
go func() {
timer := time.NewTimer(b.interval)
timer.Stop()
for {
newValue, err := b.AnalogRead(pin)
if err != nil {
b.Publish(b.Event(gpio.Error), err)
} else if newValue != value && newValue != -1 {
value = newValue
b.Publish(b.Event("analogread-" + pin), value)
}
timer.Reset(b.interval)
select {
case <-timer.C:
case <-b.halt:
timer.Stop()
return
}
}
}()
return b.Subscribe()
}
// I2cStart starts a i2c device in specified address on i2c bus /dev/i2c-1
func (b *Adaptor) I2cStart(address int) (err error) {
if b.i2cDevice == nil {