From 3e9e12582a3d8a84503a717f4450daab0627c1ff Mon Sep 17 00:00:00 2001 From: disaster Date: Tue, 11 Feb 2020 07:58:14 +0100 Subject: [PATCH] Add INPUT_PULLUP support on firmdata platform Add the availability to use INPUT_PULLUP on some GPIO driver: - button - direct_pin - makey_button - pir_motion_driver --- drivers/gpio/aip1640_driver.go | 4 +- drivers/gpio/button_driver.go | 44 +++++++++-- drivers/gpio/button_driver_test.go | 89 ++++++++++++++++++++++ drivers/gpio/direct_pin_driver.go | 43 +++++++++-- drivers/gpio/direct_pin_driver_test.go | 42 +++++++++++ drivers/gpio/easy_driver_test.go | 1 - drivers/gpio/gpio.go | 8 ++ drivers/gpio/helpers_test.go | 41 ++++++++++ drivers/gpio/makey_button_driver.go | 56 ++++++++++---- drivers/gpio/makey_button_driver_test.go | 92 +++++++++++++++++++++++ drivers/gpio/pir_motion_driver.go | 55 ++++++++++---- drivers/gpio/pir_motion_driver_test.go | 73 ++++++++++++++++++ drivers/gpio/tm1638_driver.go | 4 +- drivers/i2c/adxl345_driver_test.go | 1 - drivers/i2c/bh1750_driver.go | 4 +- drivers/i2c/bh1750_driver_test.go | 1 - go.sum | 1 + platforms/firmata/client/client.go | 15 ++-- platforms/firmata/firmata_adaptor.go | 32 ++++++++ platforms/firmata/firmata_adaptor_test.go | 39 +++++++--- platforms/leap/leap_motion_driver.go | 4 +- platforms/leap/parser.go | 68 ++++++++--------- 22 files changed, 612 insertions(+), 105 deletions(-) diff --git a/drivers/gpio/aip1640_driver.go b/drivers/gpio/aip1640_driver.go index 77dd847b2..54041cadf 100644 --- a/drivers/gpio/aip1640_driver.go +++ b/drivers/gpio/aip1640_driver.go @@ -21,7 +21,7 @@ const ( // // Library ported from: https://github.com/wemos/WEMOS_Matrix_LED_Shield_Arduino_Library type AIP1640Driver struct { - pinClock *DirectPinDriver + pinClock *DirectPinDriver pinData *DirectPinDriver name string intensity byte @@ -34,7 +34,7 @@ type AIP1640Driver struct { func NewAIP1640Driver(a gobot.Connection, clockPin string, dataPin string) *AIP1640Driver { t := &AIP1640Driver{ name: gobot.DefaultName("AIP1640Driver"), - pinClock: NewDirectPinDriver(a, clockPin), + pinClock: NewDirectPinDriver(a, clockPin), pinData: NewDirectPinDriver(a, dataPin), intensity: 7, connection: a, diff --git a/drivers/gpio/button_driver.go b/drivers/gpio/button_driver.go index 68e1585ab..583436408 100644 --- a/drivers/gpio/button_driver.go +++ b/drivers/gpio/button_driver.go @@ -8,13 +8,15 @@ import ( // ButtonDriver Represents a digital Button type ButtonDriver struct { - Active bool - DefaultState int - pin string - name string - halt chan bool - interval time.Duration - connection DigitalReader + Active bool + DefaultState int + pin string + name string + halt chan bool + interval time.Duration + connection DigitalReader + connectionInputPullup DigitalReaderInputPullup + inputPullup bool gobot.Eventer } @@ -33,6 +35,7 @@ func NewButtonDriver(a DigitalReader, pin string, v ...time.Duration) *ButtonDri Eventer: gobot.NewEventer(), interval: 10 * time.Millisecond, halt: make(chan bool), + inputPullup: false, } if len(v) > 0 { @@ -56,7 +59,14 @@ func (b *ButtonDriver) Start() (err error) { state := b.DefaultState go func() { for { - newValue, err := b.connection.DigitalRead(b.Pin()) + var newValue int + var err error + if b.IsInputPullup() { + newValue, err = b.connectionInputPullup.DigitalReadInputPullup(b.Pin()) + } else { + newValue, err = b.connection.DigitalRead(b.Pin()) + } + if err != nil { b.Publish(Error, err) } else if newValue != state && newValue != -1 { @@ -73,6 +83,24 @@ func (b *ButtonDriver) Start() (err error) { return } +// SetInputPullup permit to put pin mode as INPUT_PULLUP +// Your pletaform must be support it +func (b *ButtonDriver) SetInputPullup() (err error) { + if reader, ok := b.Connection().(DigitalReaderInputPullup); ok { + b.connectionInputPullup = reader + b.inputPullup = true + + return + } + + err = ErrDigitalReadInputPullupUnsupported + + return +} + +// IsInputPullup return if pin is setting as INPUT_PULLUP +func (b *ButtonDriver) IsInputPullup() bool { return b.inputPullup } + // Halt stops polling the button for new information func (b *ButtonDriver) Halt() (err error) { b.halt <- true diff --git a/drivers/gpio/button_driver_test.go b/drivers/gpio/button_driver_test.go index 214046281..4c39253c0 100644 --- a/drivers/gpio/button_driver_test.go +++ b/drivers/gpio/button_driver_test.go @@ -18,6 +18,10 @@ func initTestButtonDriver() *ButtonDriver { return NewButtonDriver(newGpioTestAdaptor(), "1") } +func initTestButtonInputPullupDriver() *ButtonDriver { + return NewButtonDriver(newGpioInputPullupTestAdaptor(), "1") +} + func TestButtonDriverHalt(t *testing.T) { d := initTestButtonDriver() go func() { @@ -157,3 +161,88 @@ func TestButtonDriverSetName(t *testing.T) { g.SetName("mybot") gobottest.Assert(t, g.Name(), "mybot") } + +func TestButtonDriverSetInputPullup(t *testing.T) { + g := initTestButtonInputPullupDriver() + err := g.SetInputPullup() + gobottest.Assert(t, err, nil) + gobottest.Assert(t, g.IsInputPullup(), true) + + g = initTestButtonDriver() + err = g.SetInputPullup() + gobottest.Assert(t, err, ErrDigitalReadInputPullupUnsupported) +} + +func TestButtonInputPullupDriverStart(t *testing.T) { + sem := make(chan bool, 0) + a := newGpioInputPullupTestAdaptor() + d := NewButtonDriver(a, "1") + err := d.SetInputPullup() + gobottest.Assert(t, err, nil) + + d.Once(ButtonPush, func(data interface{}) { + gobottest.Assert(t, d.Active, true) + sem <- true + }) + + a.TestAdaptorDigitalRead(func() (val int, err error) { + val = 1 + return + }) + + gobottest.Assert(t, d.Start(), nil) + + select { + case <-sem: + case <-time.After(buttonTestDelay * time.Millisecond): + t.Errorf("Button Event \"Push\" was not published") + } + + d.Once(ButtonRelease, func(data interface{}) { + gobottest.Assert(t, d.Active, false) + sem <- true + }) + + a.TestAdaptorDigitalRead(func() (val int, err error) { + val = 0 + return + }) + + select { + case <-sem: + case <-time.After(buttonTestDelay * time.Millisecond): + t.Errorf("Button Event \"Release\" was not published") + } + + d.Once(Error, func(data interface{}) { + sem <- true + }) + + a.TestAdaptorDigitalRead(func() (val int, err error) { + err = errors.New("digital read error") + return + }) + + select { + case <-sem: + case <-time.After(buttonTestDelay * time.Millisecond): + t.Errorf("Button Event \"Error\" was not published") + } + + d.Once(ButtonPush, func(data interface{}) { + sem <- true + }) + + d.halt <- true + + a.TestAdaptorDigitalRead(func() (val int, err error) { + val = 1 + return + }) + + select { + case <-sem: + t.Errorf("Button Event \"Press\" should not published") + case <-time.After(buttonTestDelay * time.Millisecond): + } +} diff --git a/drivers/gpio/direct_pin_driver.go b/drivers/gpio/direct_pin_driver.go index 31e6f452c..4d3c4c86e 100644 --- a/drivers/gpio/direct_pin_driver.go +++ b/drivers/gpio/direct_pin_driver.go @@ -8,9 +8,11 @@ import ( // DirectPinDriver represents a GPIO pin type DirectPinDriver struct { - name string - pin string - connection gobot.Connection + name string + pin string + inputPullup bool + connection gobot.Connection + connectionInputPullup DigitalReaderInputPullup gobot.Commander } @@ -24,10 +26,11 @@ type DirectPinDriver struct { // "ServoWrite" - See DirectPinDriver.ServoWrite func NewDirectPinDriver(a gobot.Connection, pin string) *DirectPinDriver { d := &DirectPinDriver{ - name: gobot.DefaultName("DirectPin"), - connection: a, - pin: pin, - Commander: gobot.NewCommander(), + name: gobot.DefaultName("DirectPin"), + connection: a, + pin: pin, + Commander: gobot.NewCommander(), + inputPullup: false, } d.AddCommand("DigitalRead", func(params map[string]interface{}) interface{} { @@ -50,6 +53,25 @@ func NewDirectPinDriver(a gobot.Connection, pin string) *DirectPinDriver { return d } +// SetInputPullup permit to put pin mode as INPUT_PULLUP +// Your pletaform must be support it +func (d *DirectPinDriver) SetInputPullup() (err error) { + + if reader, ok := d.Connection().(DigitalReaderInputPullup); ok { + d.connectionInputPullup = reader + d.inputPullup = true + + return + } + + err = ErrDigitalReadInputPullupUnsupported + + return +} + +// IsInputPullup return if pin is setting as INPUT_PULLUP +func (d *DirectPinDriver) IsInputPullup() bool { return d.inputPullup } + // Name returns the DirectPinDrivers name func (d *DirectPinDriver) Name() string { return d.name } @@ -88,10 +110,17 @@ func (d *DirectPinDriver) On() (err error) { // DigitalRead returns the current digital state of the pin func (d *DirectPinDriver) DigitalRead() (val int, err error) { + + if d.IsInputPullup() { + return d.connectionInputPullup.DigitalReadInputPullup(d.Pin()) + } + if reader, ok := d.Connection().(DigitalReader); ok { return reader.DigitalRead(d.Pin()) + } err = ErrDigitalReadUnsupported + return } diff --git a/drivers/gpio/direct_pin_driver_test.go b/drivers/gpio/direct_pin_driver_test.go index 3212d5110..fa6a59f48 100644 --- a/drivers/gpio/direct_pin_driver_test.go +++ b/drivers/gpio/direct_pin_driver_test.go @@ -29,6 +29,28 @@ func initTestDirectPinDriver() *DirectPinDriver { return NewDirectPinDriver(a, "1") } +func initTestDirectPinInputPullupDriver() *DirectPinDriver { + a := newGpioInputPullupTestAdaptor() + a.testAdaptorDigitalRead = func() (val int, err error) { + val = 1 + return + } + a.testAdaptorDigitalReadInputPullup = func() (val int, err error) { + val = 1 + return + } + a.testAdaptorDigitalWrite = func() (err error) { + return errors.New("write error") + } + a.testAdaptorPwmWrite = func() (err error) { + return errors.New("write error") + } + a.testAdaptorServoWrite = func() (err error) { + return errors.New("write error") + } + return NewDirectPinDriver(a, "1") +} + func TestDirectPinDriver(t *testing.T) { var ret map[string]interface{} var err interface{} @@ -169,3 +191,23 @@ func TestDirectPinDriverSetName(t *testing.T) { d.SetName("mybot") gobottest.Assert(t, d.Name(), "mybot") } + +func TestDirectPinDriverSetInputPullup(t *testing.T) { + d := initTestDirectPinInputPullupDriver() + err := d.SetInputPullup() + gobottest.Assert(t, err, nil) + gobottest.Assert(t, d.IsInputPullup(), true) + + d = initTestDirectPinDriver() + err = d.SetInputPullup() + gobottest.Assert(t, err, ErrDigitalReadInputPullupUnsupported) +} + +func TestDirectPinDriverDigitalReadInputPullup(t *testing.T) { + d := initTestDirectPinInputPullupDriver() + err := d.SetInputPullup() + gobottest.Assert(t, err, nil) + ret, err := d.DigitalRead() + gobottest.Assert(t, ret, 1) + gobottest.Assert(t, err, nil) +} diff --git a/drivers/gpio/easy_driver_test.go b/drivers/gpio/easy_driver_test.go index f96358d59..a98bfb285 100644 --- a/drivers/gpio/easy_driver_test.go +++ b/drivers/gpio/easy_driver_test.go @@ -183,4 +183,3 @@ func TestEasyDriverDisable(t *testing.T) { gobottest.Assert(t, d.IsEnabled(), false) gobottest.Assert(t, d.IsMoving(), false) } - diff --git a/drivers/gpio/gpio.go b/drivers/gpio/gpio.go index 624cca19f..555bb6216 100644 --- a/drivers/gpio/gpio.go +++ b/drivers/gpio/gpio.go @@ -20,6 +20,9 @@ var ( // ErrDigitalReadUnsupported is the error resulting when a driver attempts to use // hardware capabilities which a connection does not support ErrDigitalReadUnsupported = errors.New("DigitalRead is not supported by this platform") + // ErrDigitalReadInputPullupUnsupported is the error resulting when a driver attempts to use + // hardware capabilities which a connection does not support + ErrDigitalReadInputPullupUnsupported = errors.New("DigitalRead with INPUT_PULLUP is not supported by this platform") // ErrServoOutOfRange is the error resulting when a driver attempts to use // hardware capabilities which a connection does not support ErrServoOutOfRange = errors.New("servo angle must be between 0-180") @@ -61,3 +64,8 @@ type DigitalWriter interface { type DigitalReader interface { DigitalRead(string) (val int, err error) } + +// DigitalReaderInputPullup interface represents an Adaptor which has DigitalRead capabilities with INPUT_PULLUP +type DigitalReaderInputPullup interface { + DigitalReadInputPullup(string) (val int, err error) +} diff --git a/drivers/gpio/helpers_test.go b/drivers/gpio/helpers_test.go index c7261bbdd..ffb8368a1 100644 --- a/drivers/gpio/helpers_test.go +++ b/drivers/gpio/helpers_test.go @@ -26,6 +26,11 @@ type gpioTestAdaptor struct { testAdaptorDigitalRead func() (val int, err error) } +type gpioInputPullupTestAdaptor struct { + testAdaptorDigitalReadInputPullup func() (val int, err error) + gpioTestAdaptor +} + func (t *gpioTestAdaptor) TestAdaptorDigitalWrite(f func() (err error)) { t.mtx.Lock() defer t.mtx.Unlock() @@ -103,3 +108,39 @@ func newGpioTestAdaptor() *gpioTestAdaptor { }, } } + +func (t *gpioInputPullupTestAdaptor) TestAdaptorDigitalReadInputPullup(f func() (val int, err error)) { + t.mtx.Lock() + defer t.mtx.Unlock() + t.testAdaptorDigitalReadInputPullup = f +} +func (t *gpioInputPullupTestAdaptor) DigitalReadInputPullup(string) (val int, err error) { + t.mtx.Lock() + defer t.mtx.Unlock() + return t.testAdaptorDigitalRead() +} +func newGpioInputPullupTestAdaptor() *gpioInputPullupTestAdaptor { + return &gpioInputPullupTestAdaptor{ + gpioTestAdaptor: gpioTestAdaptor{ + port: "/dev/null", + testAdaptorDigitalWrite: func() (err error) { + return nil + }, + testAdaptorServoWrite: func() (err error) { + return nil + }, + testAdaptorPwmWrite: func() (err error) { + return nil + }, + testAdaptorAnalogRead: func() (val int, err error) { + return 99, nil + }, + testAdaptorDigitalRead: func() (val int, err error) { + return 1, nil + }, + }, + testAdaptorDigitalReadInputPullup: func() (val int, err error) { + return 1, nil + }, + } +} diff --git a/drivers/gpio/makey_button_driver.go b/drivers/gpio/makey_button_driver.go index 412a3ba11..269ffb4f5 100644 --- a/drivers/gpio/makey_button_driver.go +++ b/drivers/gpio/makey_button_driver.go @@ -8,12 +8,14 @@ import ( // MakeyButtonDriver Represents a Makey Button type MakeyButtonDriver struct { - name string - pin string - halt chan bool - connection DigitalReader - Active bool - interval time.Duration + name string + pin string + halt chan bool + connection DigitalReader + connectionInputPullup DigitalReaderInputPullup + Active bool + inputPullup bool + interval time.Duration gobot.Eventer } @@ -24,13 +26,14 @@ type MakeyButtonDriver struct { // 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), + name: gobot.DefaultName("MakeyButton"), + connection: a, + pin: pin, + Active: false, + Eventer: gobot.NewEventer(), + interval: 10 * time.Millisecond, + halt: make(chan bool), + inputPullup: false, } if len(v) > 0 { @@ -44,6 +47,24 @@ func NewMakeyButtonDriver(a DigitalReader, pin string, v ...time.Duration) *Make return m } +// SetInputPullup permit to put pin mode as INPUT_PULLUP +// Your pletaform must be support it +func (b *MakeyButtonDriver) SetInputPullup() (err error) { + if reader, ok := b.Connection().(DigitalReaderInputPullup); ok { + b.connectionInputPullup = reader + b.inputPullup = true + + return + } + + err = ErrDigitalReadInputPullupUnsupported + + return +} + +// IsInputPullup return if pin is setting as INPUT_PULLUP +func (b *MakeyButtonDriver) IsInputPullup() bool { return b.inputPullup } + // Name returns the MakeyButtonDrivers name func (b *MakeyButtonDriver) Name() string { return b.name } @@ -68,7 +89,14 @@ func (b *MakeyButtonDriver) Start() (err error) { timer := time.NewTimer(b.interval) timer.Stop() for { - newValue, err := b.connection.DigitalRead(b.Pin()) + var newValue int + var err error + if b.IsInputPullup() { + newValue, err = b.connectionInputPullup.DigitalReadInputPullup(b.Pin()) + } else { + newValue, err = b.connection.DigitalRead(b.Pin()) + } + if err != nil { b.Publish(Error, err) } else if newValue != state && newValue != -1 { diff --git a/drivers/gpio/makey_button_driver_test.go b/drivers/gpio/makey_button_driver_test.go index bb9b2c083..a8d580e7a 100644 --- a/drivers/gpio/makey_button_driver_test.go +++ b/drivers/gpio/makey_button_driver_test.go @@ -17,6 +17,10 @@ func initTestMakeyButtonDriver() *MakeyButtonDriver { return NewMakeyButtonDriver(newGpioTestAdaptor(), "1") } +func initTestMakeyButtonInputPullupDriver() *MakeyButtonDriver { + return NewMakeyButtonDriver(newGpioInputPullupTestAdaptor(), "1") +} + func TestMakeyButtonDriverHalt(t *testing.T) { d := initTestMakeyButtonDriver() done := make(chan struct{}) @@ -115,3 +119,91 @@ func TestMakeyButtonDriverStart(t *testing.T) { case <-time.After(makeyTestDelay * time.Millisecond): } } + +func TestMakeyButtonDriverSetInputPullup(t *testing.T) { + d := initTestMakeyButtonInputPullupDriver() + err := d.SetInputPullup() + gobottest.Assert(t, err, nil) + gobottest.Assert(t, d.IsInputPullup(), true) + + d = initTestMakeyButtonDriver() + err = d.SetInputPullup() + gobottest.Assert(t, err, ErrDigitalReadInputPullupUnsupported) +} + +func TestMakeyButtonInputPullupDriverStart(t *testing.T) { + sem := make(chan bool) + a := newGpioInputPullupTestAdaptor() + d := NewMakeyButtonDriver(a, "1") + + err := d.SetInputPullup() + gobottest.Assert(t, err, nil) + + gobottest.Assert(t, d.Start(), nil) + + d.Once(ButtonPush, func(data interface{}) { + gobottest.Assert(t, d.Active, true) + sem <- true + }) + + a.TestAdaptorDigitalRead(func() (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{}) { + gobottest.Assert(t, d.Active, false) + sem <- true + }) + + a.TestAdaptorDigitalRead(func() (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{}) { + gobottest.Assert(t, data.(error).Error(), "digital read error") + sem <- true + }) + + a.TestAdaptorDigitalRead(func() (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.TestAdaptorDigitalRead(func() (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): + } +} diff --git a/drivers/gpio/pir_motion_driver.go b/drivers/gpio/pir_motion_driver.go index 97d7ad1bb..b94c7fa76 100644 --- a/drivers/gpio/pir_motion_driver.go +++ b/drivers/gpio/pir_motion_driver.go @@ -8,12 +8,14 @@ import ( // PIRMotionDriver represents a digital Proximity Infra Red (PIR) motion detecter type PIRMotionDriver struct { - Active bool - pin string - name string - halt chan bool - interval time.Duration - connection DigitalReader + Active bool + pin string + name string + halt chan bool + interval time.Duration + inputPullup bool + connection DigitalReader + connectionInputPullup DigitalReaderInputPullup gobot.Eventer } @@ -24,13 +26,14 @@ type PIRMotionDriver struct { // time.Duration: Interval at which the PIRMotionDriver is polled for new information func NewPIRMotionDriver(a DigitalReader, pin string, v ...time.Duration) *PIRMotionDriver { b := &PIRMotionDriver{ - name: gobot.DefaultName("PIRMotion"), - connection: a, - pin: pin, - Active: false, - Eventer: gobot.NewEventer(), - interval: 10 * time.Millisecond, - halt: make(chan bool), + name: gobot.DefaultName("PIRMotion"), + connection: a, + pin: pin, + Active: false, + Eventer: gobot.NewEventer(), + interval: 10 * time.Millisecond, + halt: make(chan bool), + inputPullup: false, } if len(v) > 0 { @@ -58,7 +61,13 @@ func NewPIRMotionDriver(a DigitalReader, pin string, v ...time.Duration) *PIRMot func (p *PIRMotionDriver) Start() (err error) { go func() { for { - newValue, err := p.connection.DigitalRead(p.Pin()) + var newValue int + var err error + if p.IsInputPullup() { + newValue, err = p.connectionInputPullup.DigitalReadInputPullup(p.Pin()) + } else { + newValue, err = p.connection.DigitalRead(p.Pin()) + } if err != nil { p.Publish(Error, err) } @@ -85,6 +94,24 @@ func (p *PIRMotionDriver) Start() (err error) { return } +// SetInputPullup permit to put pin mode as INPUT_PULLUP +// Your pletaform must be support it +func (p *PIRMotionDriver) SetInputPullup() (err error) { + if reader, ok := p.Connection().(DigitalReaderInputPullup); ok { + p.connectionInputPullup = reader + p.inputPullup = true + + return + } + + err = ErrDigitalReadInputPullupUnsupported + + return +} + +// IsInputPullup return if pin is setting as INPUT_PULLUP +func (p *PIRMotionDriver) IsInputPullup() bool { return p.inputPullup } + // Halt stops polling the button for new information func (p *PIRMotionDriver) Halt() (err error) { p.halt <- true diff --git a/drivers/gpio/pir_motion_driver_test.go b/drivers/gpio/pir_motion_driver_test.go index 32de7ec1c..1136e54da 100644 --- a/drivers/gpio/pir_motion_driver_test.go +++ b/drivers/gpio/pir_motion_driver_test.go @@ -18,6 +18,10 @@ func initTestPIRMotionDriver() *PIRMotionDriver { return NewPIRMotionDriver(newGpioTestAdaptor(), "1") } +func initTestPIRMotionInputPullupDriver() *PIRMotionDriver { + return NewPIRMotionDriver(newGpioInputPullupTestAdaptor(), "1") +} + func TestPIRMotionDriverHalt(t *testing.T) { d := initTestPIRMotionDriver() go func() { @@ -99,3 +103,72 @@ func TestPIRDriverSetName(t *testing.T) { d.SetName("mybot") gobottest.Assert(t, d.Name(), "mybot") } + +func TestPIRDriverSetInputPullup(t *testing.T) { + d := initTestPIRMotionInputPullupDriver() + err := d.SetInputPullup() + gobottest.Assert(t, err, nil) + gobottest.Assert(t, d.IsInputPullup(), true) + + d = initTestPIRMotionDriver() + err = d.SetInputPullup() + gobottest.Assert(t, err, ErrDigitalReadInputPullupUnsupported) +} + +func TestPIRMotionInputPullupDriverStart(t *testing.T) { + sem := make(chan bool, 0) + a := newGpioInputPullupTestAdaptor() + d := NewPIRMotionDriver(a, "1") + + err := d.SetInputPullup() + gobottest.Assert(t, err, nil) + + gobottest.Assert(t, d.Start(), nil) + + d.Once(MotionDetected, func(data interface{}) { + gobottest.Assert(t, d.Active, true) + sem <- true + }) + + a.TestAdaptorDigitalRead(func() (val int, err error) { + val = 1 + return + }) + + select { + case <-sem: + case <-time.After(motionTestDelay * time.Millisecond): + t.Errorf("PIRMotionDriver Event \"MotionDetected\" was not published") + } + + d.Once(MotionStopped, func(data interface{}) { + gobottest.Assert(t, d.Active, false) + sem <- true + }) + + a.TestAdaptorDigitalRead(func() (val int, err error) { + val = 0 + return + }) + + select { + case <-sem: + case <-time.After(motionTestDelay * time.Millisecond): + t.Errorf("PIRMotionDriver Event \"MotionStopped\" was not published") + } + + d.Once(Error, func(data interface{}) { + sem <- true + }) + + a.TestAdaptorDigitalRead(func() (val int, err error) { + err = errors.New("digital read error") + return + }) + + select { + case <-sem: + case <-time.After(motionTestDelay * time.Millisecond): + t.Errorf("PIRMotionDriver Event \"Error\" was not published") + } +} diff --git a/drivers/gpio/tm1638_driver.go b/drivers/gpio/tm1638_driver.go index 246dd6491..a46a2b4ad 100644 --- a/drivers/gpio/tm1638_driver.go +++ b/drivers/gpio/tm1638_driver.go @@ -33,7 +33,7 @@ const ( // Ported from the Arduino driver https://github.com/rjbatista/tm1638-library type TM1638Driver struct { - pinClock *DirectPinDriver + pinClock *DirectPinDriver pinData *DirectPinDriver pinStrobe *DirectPinDriver fonts map[string]byte @@ -46,7 +46,7 @@ type TM1638Driver struct { func NewTM1638Driver(a gobot.Connection, clockPin string, dataPin string, strobePin string) *TM1638Driver { t := &TM1638Driver{ name: gobot.DefaultName("TM1638"), - pinClock: NewDirectPinDriver(a, clockPin), + pinClock: NewDirectPinDriver(a, clockPin), pinData: NewDirectPinDriver(a, dataPin), pinStrobe: NewDirectPinDriver(a, strobePin), fonts: NewTM1638Fonts(), diff --git a/drivers/i2c/adxl345_driver_test.go b/drivers/i2c/adxl345_driver_test.go index b9ae464d4..825afad58 100644 --- a/drivers/i2c/adxl345_driver_test.go +++ b/drivers/i2c/adxl345_driver_test.go @@ -116,7 +116,6 @@ func TestADXL345DriverXYZError(t *testing.T) { gobottest.Assert(t, err, errors.New("read error")) } - func TestADXL345DriverRawXYZ(t *testing.T) { d, adaptor := initTestADXL345DriverWithStubbedAdaptor() d.Start() diff --git a/drivers/i2c/bh1750_driver.go b/drivers/i2c/bh1750_driver.go index f9cafae4e..040d58e45 100644 --- a/drivers/i2c/bh1750_driver.go +++ b/drivers/i2c/bh1750_driver.go @@ -1,8 +1,8 @@ package i2c import ( - "time" "errors" + "time" "gobot.io/x/gobot" ) @@ -44,7 +44,7 @@ func NewBH1750Driver(a Connector, options ...func(Config)) *BH1750Driver { name: gobot.DefaultName("BH1750"), connector: a, Config: NewConfig(), - mode: BH1750_CONTINUOUS_HIGH_RES_MODE, + mode: BH1750_CONTINUOUS_HIGH_RES_MODE, } for _, option := range options { diff --git a/drivers/i2c/bh1750_driver_test.go b/drivers/i2c/bh1750_driver_test.go index 77f277fc8..53b73db41 100644 --- a/drivers/i2c/bh1750_driver_test.go +++ b/drivers/i2c/bh1750_driver_test.go @@ -145,4 +145,3 @@ func TestBH1750DriverRawSensorDataError(t *testing.T) { _, err := d.RawSensorData() gobottest.Assert(t, err, errors.New("wrong number of bytes read")) } - diff --git a/go.sum b/go.sum index a64ba2e14..1f87885c9 100644 --- a/go.sum +++ b/go.sum @@ -77,6 +77,7 @@ go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45 h1:mACY1anK6HNCZtm/DK2Rf2 go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45/go.mod h1:dRSl/CVCTf56CkXgJMDOdSwNfo2g1orOGE/gBGdvjZw= gocv.io/x/gocv v0.20.0 h1:2q75zQ8Zel2tB69G6qrmf/E7EdvaCs90qvkHzdSBOAg= gocv.io/x/gocv v0.20.0/go.mod h1:vZETJRwLnl11muQ6iL3q4ju+0oJRrdmYdv5xJTH7WYA= +gocv.io/x/gocv v0.21.0 h1:dVjagrupZrfCRY0qPEaYWgoNMRpBel6GYDH4mvQOK8Y= gocv.io/x/gocv v0.21.0/go.mod h1:Rar2PS6DV+T4FL+PM535EImD/h13hGVaHhnCu1xarBs= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= diff --git a/platforms/firmata/client/client.go b/platforms/firmata/client/client.go index 94296b5d5..3d322d446 100644 --- a/platforms/firmata/client/client.go +++ b/platforms/firmata/client/client.go @@ -16,11 +16,12 @@ import ( // Pin Modes const ( - Input = 0x00 - Output = 0x01 - Analog = 0x02 - Pwm = 0x03 - Servo = 0x04 + Input = 0x00 + Output = 0x01 + Analog = 0x02 + Pwm = 0x03 + Servo = 0x04 + InputPullup = 0x0B ) // Sysex Codes @@ -411,7 +412,7 @@ func (b *Client) process() (err error) { for i := 0; i < 8; i++ { pinNumber := int((8*byte(port) + byte(i))) if len(b.pins) > pinNumber { - if b.pins[pinNumber].Mode == Input { + if b.pins[pinNumber].Mode == Input || b.pins[pinNumber].Mode == InputPullup { b.pins[pinNumber].Value = int((portValue >> (byte(i) & 0x07)) & 0x01) b.Publish(b.Event(fmt.Sprintf("DigitalRead%v", pinNumber)), b.pins[pinNumber].Value) } @@ -444,7 +445,7 @@ func (b *Client) process() (err error) { for _, val := range currentBuffer[2 : len(currentBuffer)-1] { if val == 127 { modes := []int{} - for _, mode := range []int{Input, Output, Analog, Pwm, Servo} { + for _, mode := range []int{Input, InputPullup, Output, Analog, Pwm, Servo} { if (supportedModes & (1 << byte(mode))) != 0 { modes = append(modes, mode) } diff --git a/platforms/firmata/firmata_adaptor.go b/platforms/firmata/firmata_adaptor.go index 6975df4b7..db8791ec2 100644 --- a/platforms/firmata/firmata_adaptor.go +++ b/platforms/firmata/firmata_adaptor.go @@ -45,6 +45,7 @@ type Adaptor struct { Board firmataBoard conn io.ReadWriteCloser PortOpener func(port string) (io.ReadWriteCloser, error) + inputMode int gobot.Eventer } @@ -57,6 +58,7 @@ type Adaptor struct { // to a serial port with a baude rate of 57600. If an io.ReadWriteCloser // is supplied, then the Adaptor will use the provided io.ReadWriteCloser and use the // string port as a label to be displayed in the log and api. +// If an int is not suplied, The Adaptor will set pin to INPUT for read on digital pin. func NewAdaptor(args ...interface{}) *Adaptor { f := &Adaptor{ name: gobot.DefaultName("Firmata"), @@ -205,6 +207,36 @@ func (f *Adaptor) DigitalRead(pin string) (val int, err error) { } return f.Board.Pins()[p].Value, nil + +} + +// DigitalReadInputPullup retrieves digital value from specified pin with INPUT_PULLUP mode. +// Returns -1 if the response from the board has timed out +func (f *Adaptor) DigitalReadInputPullup(pin string) (val int, err error) { + p, err := strconv.Atoi(pin) + if err != nil { + return + } + + if f.Board.Pins()[p].Mode != client.InputPullup { + if err = f.Board.SetPinMode(p, client.InputPullup); err != nil { + return + } + if err = f.Board.ReportDigital(p, 1); err != nil { + return + } + <-time.After(10 * time.Millisecond) + } + + // On INPUT_PULLUP, we need to reverse value + switch f.Board.Pins()[p].Value { + case 0: + val = 1 + case 1: + val = 0 + } + + return } // AnalogRead retrieves value from analog pin. diff --git a/platforms/firmata/firmata_adaptor_test.go b/platforms/firmata/firmata_adaptor_test.go index e00836573..3fac6254e 100644 --- a/platforms/firmata/firmata_adaptor_test.go +++ b/platforms/firmata/firmata_adaptor_test.go @@ -20,6 +20,7 @@ import ( // make sure that this Adaptor fullfills all the required interfaces var _ gobot.Adaptor = (*Adaptor)(nil) var _ gpio.DigitalReader = (*Adaptor)(nil) +var _ gpio.DigitalReaderInputPullup = (*Adaptor)(nil) var _ gpio.DigitalWriter = (*Adaptor)(nil) var _ aio.AnalogReader = (*Adaptor)(nil) var _ gpio.PwmWriter = (*Adaptor)(nil) @@ -78,16 +79,17 @@ func (m mockFirmataBoard) Disconnect() error { func (m mockFirmataBoard) Pins() []client.Pin { return m.pins } -func (mockFirmataBoard) AnalogWrite(int, int) error { return nil } -func (mockFirmataBoard) SetPinMode(int, int) error { return nil } -func (mockFirmataBoard) ReportAnalog(int, int) error { return nil } -func (mockFirmataBoard) ReportDigital(int, int) error { return nil } -func (mockFirmataBoard) DigitalWrite(int, int) error { return nil } -func (mockFirmataBoard) I2cRead(int, int) error { return nil } -func (mockFirmataBoard) I2cWrite(int, []byte) error { return nil } -func (mockFirmataBoard) I2cConfig(int) error { return nil } -func (mockFirmataBoard) ServoConfig(int, int, int) error { return nil } -func (mockFirmataBoard) WriteSysex(data []byte) error { return nil } +func (mockFirmataBoard) AnalogWrite(int, int) error { return nil } +func (mockFirmataBoard) SetPinMode(int, int) error { return nil } +func (mockFirmataBoard) ReportAnalog(int, int) error { return nil } +func (mockFirmataBoard) ReportDigital(int, int) error { return nil } +func (mockFirmataBoard) ReportDigitalInputPullup(int, int) error { return nil } +func (mockFirmataBoard) DigitalWrite(int, int) error { return nil } +func (mockFirmataBoard) I2cRead(int, int) error { return nil } +func (mockFirmataBoard) I2cWrite(int, []byte) error { return nil } +func (mockFirmataBoard) I2cConfig(int) error { return nil } +func (mockFirmataBoard) ServoConfig(int, int, int) error { return nil } +func (mockFirmataBoard) WriteSysex(data []byte) error { return nil } func initTestAdaptor() *Adaptor { a := NewAdaptor("/dev/null") @@ -185,6 +187,23 @@ func TestAdaptorDigitalReadBadPin(t *testing.T) { gobottest.Refute(t, err, nil) } +func TestAdaptorDigitalReadInputPullup(t *testing.T) { + a := initTestAdaptor() + val, err := a.DigitalReadInputPullup("1") + gobottest.Assert(t, err, nil) + gobottest.Assert(t, val, 0) + + val, err = a.DigitalReadInputPullup("0") + gobottest.Assert(t, err, nil) + gobottest.Assert(t, val, 1) +} + +func TestAdaptorDigitalReadInputPullupBadPin(t *testing.T) { + a := initTestAdaptor() + _, err := a.DigitalReadInputPullup("xyz") + gobottest.Refute(t, err, nil) +} + func TestAdaptorAnalogRead(t *testing.T) { a := initTestAdaptor() val, err := a.AnalogRead("1") diff --git a/platforms/leap/leap_motion_driver.go b/platforms/leap/leap_motion_driver.go index ce4f1958b..742836766 100644 --- a/platforms/leap/leap_motion_driver.go +++ b/platforms/leap/leap_motion_driver.go @@ -83,11 +83,11 @@ func enableFeature(l *Driver, feature string) (err error) { // "hand" - Emits Hand when detected in message from Leap. // "gesture" - Emits Gesture when detected in message from Leap. func (l *Driver) Start() (err error) { - err = enableFeature(l,"enableGestures") + err = enableFeature(l, "enableGestures") if err != nil { return err } - err = enableFeature(l,"background") + err = enableFeature(l, "background") if err != nil { return err } diff --git a/platforms/leap/parser.go b/platforms/leap/parser.go index 53d615a90..3214b66d8 100644 --- a/platforms/leap/parser.go +++ b/platforms/leap/parser.go @@ -6,20 +6,20 @@ import ( // Gesture is a Leap Motion gesture that has been detected type Gesture struct { - Center []float64 `json:"center"` - Direction []float64 `json:"direction"` - Duration int `json:"duration"` - HandIDs []int `json:"handIds"` - ID int `json:"id"` - Normal []float64 `json:"normal"` - PointableIDs []int `json:"pointableIds"` - Position []float64 `json:"position"` - Progress float64 `json:"progress"` - Radius float64 `json:"radius"` - Speed float64 `json:"speed"` - StartPosition []float64 `json:"StartPosition"` - State string `json:"state"` - Type string `json:"type"` + Center []float64 `json:"center"` + Direction []float64 `json:"direction"` + Duration int `json:"duration"` + HandIDs []int `json:"handIds"` + ID int `json:"id"` + Normal []float64 `json:"normal"` + PointableIDs []int `json:"pointableIds"` + Position []float64 `json:"position"` + Progress float64 `json:"progress"` + Radius float64 `json:"radius"` + Speed float64 `json:"speed"` + StartPosition []float64 `json:"StartPosition"` + State string `json:"state"` + Type string `json:"type"` } // Hand is a Leap Motion hand that has been detected @@ -48,26 +48,26 @@ type Hand struct { // Pointable is a Leap Motion pointing motion that has been detected type Pointable struct { - Bases [][][]float64 `json:"bases"` - BTipPosition []float64 `json:"btipPosition"` - CarpPosition []float64 `json:"carpPosition"` - DipPosition []float64 `json:"dipPosition"` - Direction []float64 `json:"direction"` - Extended bool `json:"extended"` - HandID int `json:"handId"` - ID int `json:"id"` - Length float64 `json:"length"` - MCPPosition []float64 `json:"mcpPosition"` - PIPPosition []float64 `json:"pipPosition"` - StabilizedTipPosition []float64 `json:"stabilizedTipPosition"` - TimeVisible float64 `json:"timeVisible"` - TipPosition []float64 `json:"tipPosition"` - TipVelocity []float64 `json:"tipVelocity"` - Tool bool `json:"tool"` - TouchDistance float64 `json:"touchDistance"` - TouchZone string `json:"touchZone"` - Type int `json:"type"` - Width float64 `json:"width"` + Bases [][][]float64 `json:"bases"` + BTipPosition []float64 `json:"btipPosition"` + CarpPosition []float64 `json:"carpPosition"` + DipPosition []float64 `json:"dipPosition"` + Direction []float64 `json:"direction"` + Extended bool `json:"extended"` + HandID int `json:"handId"` + ID int `json:"id"` + Length float64 `json:"length"` + MCPPosition []float64 `json:"mcpPosition"` + PIPPosition []float64 `json:"pipPosition"` + StabilizedTipPosition []float64 `json:"stabilizedTipPosition"` + TimeVisible float64 `json:"timeVisible"` + TipPosition []float64 `json:"tipPosition"` + TipVelocity []float64 `json:"tipVelocity"` + Tool bool `json:"tool"` + TouchDistance float64 `json:"touchDistance"` + TouchZone string `json:"touchZone"` + Type int `json:"type"` + Width float64 `json:"width"` } // InteractionBox is the area within which the gestural interaction has been detected