diff --git a/README.md b/README.md index 04df5d02..e2b1a9e4 100644 --- a/README.md +++ b/README.md @@ -197,6 +197,7 @@ Gobot has a extensible system for connecting to hardware devices. The following - [Intel Curie](https://www.intel.com/content/www/us/en/products/boards-kits/curie.html) <=> [Package](https://github.com/hybridgroup/gobot/tree/master/platforms/intel-iot/curie) - [Intel Edison](http://www.intel.com/content/www/us/en/do-it-yourself/edison.html) <=> [Package](https://github.com/hybridgroup/gobot/tree/master/platforms/intel-iot/edison) - [Intel Joule](http://intel.com/joule/getstarted) <=> [Package](https://github.com/hybridgroup/gobot/tree/master/platforms/intel-iot/joule) +- [Jetson Nano](https://developer.nvidia.com/embedded/jetson-nano/) <=> [Package](https://github.com/hybridgroup/gobot/tree/master/platforms/jetson) - [Joystick](http://en.wikipedia.org/wiki/Joystick) <=> [Package](https://github.com/hybridgroup/gobot/tree/master/platforms/joystick) - [Keyboard](https://en.wikipedia.org/wiki/Computer_keyboard) <=> [Package](https://github.com/hybridgroup/gobot/tree/master/platforms/keyboard) - [Leap Motion](https://www.leapmotion.com/) <=> [Package](https://github.com/hybridgroup/gobot/tree/master/platforms/leap) diff --git a/platforms/adaptors/digitalpinsadaptor_test.go b/platforms/adaptors/digitalpinsadaptor_test.go index a7e4bdad..4ed598dd 100644 --- a/platforms/adaptors/digitalpinsadaptor_test.go +++ b/platforms/adaptors/digitalpinsadaptor_test.go @@ -103,12 +103,55 @@ func TestDigitalIO(t *testing.T) { mockedPaths := []string{ "/sys/class/gpio/export", "/sys/class/gpio/unexport", - "/sys/class/gpio/gpio18/value", - "/sys/class/gpio/gpio18/direction", + "/sys/class/gpio/gpio25/value", + "/sys/class/gpio/gpio25/direction", + } + a, _ := initTestAdaptorWithMockedFilesystem(mockedPaths) + a.Connect() + + err := a.DigitalWrite("14", 1) + gobottest.Assert(t, err, nil) + + i, err := a.DigitalRead("14") + gobottest.Assert(t, err, nil) + gobottest.Assert(t, i, 1) +} + +func TestDigitalRead(t *testing.T) { + mockedPaths := []string{ + "/sys/class/gpio/export", + "/sys/class/gpio/unexport", "/sys/class/gpio/gpio24/value", "/sys/class/gpio/gpio24/direction", } a, fs := initTestAdaptorWithMockedFilesystem(mockedPaths) + fs.Files["/sys/class/gpio/gpio24/value"].Contents = "1" + i, err := a.DigitalRead("13") + gobottest.Assert(t, err.Error(), "not connected") + + a.Connect() + + i, err = a.DigitalRead("13") + gobottest.Assert(t, err, nil) + gobottest.Assert(t, i, 1) + + fs.WithReadError = true + _, err = a.DigitalRead("13") + gobottest.Assert(t, err, errors.New("read error")) + + fs.WithWriteError = true + _, err = a.DigitalRead("7") + gobottest.Assert(t, err, errors.New("write error")) +} + +func TestDigitalWrite(t *testing.T) { + mockedPaths := []string{ + "/sys/class/gpio/export", + "/sys/class/gpio/unexport", + "/sys/class/gpio/gpio18/value", + "/sys/class/gpio/gpio18/direction", + } + a, fs := initTestAdaptorWithMockedFilesystem(mockedPaths) err := a.DigitalWrite("7", 1) gobottest.Assert(t, err.Error(), "not connected") @@ -118,21 +161,13 @@ func TestDigitalIO(t *testing.T) { gobottest.Assert(t, err, nil) gobottest.Assert(t, fs.Files["/sys/class/gpio/gpio18/value"].Contents, "1") - err = a.DigitalWrite("13", 1) + err = a.DigitalWrite("7", 1) gobottest.Assert(t, err, nil) - i, err := a.DigitalRead("13") - gobottest.Assert(t, err, nil) - gobottest.Assert(t, i, 1) - gobottest.Assert(t, a.DigitalWrite("notexist", 1), errors.New("not a valid pin")) - fs.WithReadError = true - _, err = a.DigitalRead("13") - gobottest.Assert(t, err, errors.New("read error")) - fs.WithWriteError = true - _, err = a.DigitalRead("7") + err = a.DigitalWrite("7", 0) gobottest.Assert(t, err, errors.New("write error")) } diff --git a/platforms/beaglebone/beaglebone_adaptor.go b/platforms/beaglebone/beaglebone_adaptor.go index 8c33aac0..048022b4 100644 --- a/platforms/beaglebone/beaglebone_adaptor.go +++ b/platforms/beaglebone/beaglebone_adaptor.go @@ -13,6 +13,7 @@ import ( "gobot.io/x/gobot" "gobot.io/x/gobot/drivers/i2c" "gobot.io/x/gobot/drivers/spi" + "gobot.io/x/gobot/platforms/adaptors" "gobot.io/x/gobot/system" ) @@ -25,10 +26,10 @@ const pwmDefaultPeriod = 500000 // Adaptor is the gobot.Adaptor representation for the Beaglebone Black/Green type Adaptor struct { - name string - sys *system.Accesser - mutex sync.Mutex - digitalPins map[string]gobot.DigitalPinner + name string + sys *system.Accesser + mutex sync.Mutex + *adaptors.DigitalPinsAdaptor pwmPins map[string]gobot.PWMPinner i2cBuses map[int]i2c.I2cDevice usrLed string @@ -42,10 +43,10 @@ type Adaptor struct { // NewAdaptor returns a new Beaglebone Black/Green Adaptor func NewAdaptor() *Adaptor { - b := &Adaptor{ + sys := system.NewAccesser() + c := &Adaptor{ name: gobot.DefaultName("BeagleboneBlack"), - sys: system.NewAccesser(), - digitalPins: make(map[string]gobot.DigitalPinner, 120), + sys: sys, pwmPins: make(map[string]gobot.PWMPinner), i2cBuses: make(map[int]i2c.I2cDevice), pinMap: bbbPinMap, @@ -59,57 +60,59 @@ func NewAdaptor() *Adaptor { analogPath: "/sys/bus/iio/devices/iio:device0", } - return b + c.DigitalPinsAdaptor = adaptors.NewDigitalPinsAdaptor(sys, c.translateAndMuxDigitalPin) + return c } // Name returns the Adaptor name -func (b *Adaptor) Name() string { return b.name } +func (c *Adaptor) Name() string { return c.name } // SetName sets the Adaptor name -func (b *Adaptor) SetName(n string) { b.name = n } +func (c *Adaptor) SetName(n string) { c.name = n } -// Connect do nothing at the moment -func (b *Adaptor) Connect() error { return nil } +// Connect create new connection to board and pins. +func (c *Adaptor) Connect() error { + c.mutex.Lock() + defer c.mutex.Unlock() + + err := c.DigitalPinsAdaptor.Connect() + return err +} // Finalize releases all i2c devices and exported analog, digital, pwm pins. -func (b *Adaptor) Finalize() (err error) { - b.mutex.Lock() - defer b.mutex.Unlock() +func (c *Adaptor) Finalize() error { + c.mutex.Lock() + defer c.mutex.Unlock() - for _, pin := range b.digitalPins { + err := c.DigitalPinsAdaptor.Finalize() + + for _, pin := range c.pwmPins { if pin != nil { if e := pin.Unexport(); e != nil { err = multierror.Append(err, e) } } } - for _, pin := range b.pwmPins { - if pin != nil { - if e := pin.Unexport(); e != nil { - err = multierror.Append(err, e) - } - } - } - for _, bus := range b.i2cBuses { + for _, bus := range c.i2cBuses { if bus != nil { if e := bus.Close(); e != nil { err = multierror.Append(err, e) } } } - for _, bus := range b.spiBuses { + for _, bus := range c.spiBuses { if bus != nil { if e := bus.Close(); e != nil { err = multierror.Append(err, e) } } } - return + return err } // PwmWrite writes the 0-254 value to the specified pin -func (b *Adaptor) PwmWrite(pin string, val byte) (err error) { - pwmPin, err := b.PWMPin(pin) +func (c *Adaptor) PwmWrite(pin string, val byte) (err error) { + pwmPin, err := c.PWMPin(pin) if err != nil { return } @@ -122,8 +125,8 @@ func (b *Adaptor) PwmWrite(pin string, val byte) (err error) { } // ServoWrite writes a servo signal to the specified pin -func (b *Adaptor) ServoWrite(pin string, angle byte) (err error) { - pwmPin, err := b.PWMPin(pin) +func (c *Adaptor) ServoWrite(pin string, angle byte) (err error) { + pwmPin, err := c.PWMPin(pin) if err != nil { return } @@ -135,26 +138,14 @@ func (b *Adaptor) ServoWrite(pin string, angle byte) (err error) { return pwmPin.SetDutyCycle(duty) } -// DigitalRead returns a digital value from specified pin -func (b *Adaptor) DigitalRead(id string) (int, error) { - b.mutex.Lock() - defer b.mutex.Unlock() - - pin, err := b.digitalPin(id, system.WithDirectionInput()) - if err != nil { - return 0, err - } - return pin.Read() -} - // DigitalWrite writes a digital value to specified pin. // valid usr pin values are usr0, usr1, usr2 and usr3 -func (b *Adaptor) DigitalWrite(id string, val byte) error { - b.mutex.Lock() - defer b.mutex.Unlock() +func (c *Adaptor) DigitalWrite(id string, val byte) error { + c.mutex.Lock() + defer c.mutex.Unlock() if strings.Contains(id, "usr") { - fi, e := b.sys.OpenFile(b.usrLed+id+"/brightness", os.O_WRONLY|os.O_APPEND, 0666) + fi, e := c.sys.OpenFile(c.usrLed+id+"/brightness", os.O_WRONLY|os.O_APPEND, 0666) defer fi.Close() if e != nil { return e @@ -163,39 +154,26 @@ func (b *Adaptor) DigitalWrite(id string, val byte) error { return err } - pin, err := b.digitalPin(id, system.WithDirectionOutput(int(val))) - if err != nil { - return err - } - return pin.Write(int(val)) -} - -// DigitalPin returns a digital pin. If the pin is initially acquired, it is an input. -// Pin direction and other options can be changed afterwards by pin.ApplyOptions() at any time. -func (b *Adaptor) DigitalPin(id string) (gobot.DigitalPinner, error) { - b.mutex.Lock() - defer b.mutex.Unlock() - - return b.digitalPin(id) + return c.DigitalPinsAdaptor.DigitalWrite(id, val) } // PWMPin returns matched pwmPin for specified pin number -func (b *Adaptor) PWMPin(pin string) (gobot.PWMPinner, error) { - b.mutex.Lock() - defer b.mutex.Unlock() +func (c *Adaptor) PWMPin(pin string) (gobot.PWMPinner, error) { + c.mutex.Lock() + defer c.mutex.Unlock() - pinInfo, err := b.translatePwmPin(pin) + pinInfo, err := c.translatePwmPin(pin) if err != nil { return nil, err } - if b.pwmPins[pin] == nil { - newPath, err := b.findPin(pinInfo.path) + if c.pwmPins[pin] == nil { + newPath, err := c.findPin(pinInfo.path) if err != nil { return nil, err } - newPin := b.sys.NewPWMPin(newPath, pinInfo.channel) - if err := b.muxPin(pin, "pwm"); err != nil { + newPin := c.sys.NewPWMPin(newPath, pinInfo.channel) + if err := c.muxPin(pin, "pwm"); err != nil { return nil, err } if err = newPin.Export(); err != nil { @@ -207,19 +185,19 @@ func (b *Adaptor) PWMPin(pin string) (gobot.PWMPinner, error) { if err = newPin.Enable(true); err != nil { return nil, err } - b.pwmPins[pin] = newPin + c.pwmPins[pin] = newPin } - return b.pwmPins[pin], nil + return c.pwmPins[pin], nil } // AnalogRead returns an analog value from specified pin -func (b *Adaptor) AnalogRead(pin string) (val int, err error) { - analogPin, err := b.translateAnalogPin(pin) +func (c *Adaptor) AnalogRead(pin string) (val int, err error) { + analogPin, err := c.translateAnalogPin(pin) if err != nil { return } - fi, err := b.sys.OpenFile(fmt.Sprintf("%v/%v", b.analogPath, analogPin), os.O_RDONLY, 0644) + fi, err := c.sys.OpenFile(fmt.Sprintf("%v/%v", c.analogPath, analogPin), os.O_RDONLY, 0644) defer fi.Close() if err != nil { @@ -238,122 +216,97 @@ func (b *Adaptor) AnalogRead(pin string) (val int, err error) { // GetConnection returns a connection to a device on a specified bus. // Valid bus number is either 0 or 2 which corresponds to /dev/i2c-0 or /dev/i2c-2. -func (b *Adaptor) GetConnection(address int, bus int) (connection i2c.Connection, err error) { - b.mutex.Lock() - defer b.mutex.Unlock() +func (c *Adaptor) GetConnection(address int, bus int) (connection i2c.Connection, err error) { + c.mutex.Lock() + defer c.mutex.Unlock() if (bus != 0) && (bus != 2) { return nil, fmt.Errorf("Bus number %d out of range", bus) } - if b.i2cBuses[bus] == nil { - b.i2cBuses[bus], err = b.sys.NewI2cDevice(fmt.Sprintf("/dev/i2c-%d", bus)) + if c.i2cBuses[bus] == nil { + c.i2cBuses[bus], err = c.sys.NewI2cDevice(fmt.Sprintf("/dev/i2c-%d", bus)) } - return i2c.NewConnection(b.i2cBuses[bus], address), err + return i2c.NewConnection(c.i2cBuses[bus], address), err } // GetDefaultBus returns the default i2c bus for this platform -func (b *Adaptor) GetDefaultBus() int { +func (c *Adaptor) GetDefaultBus() int { return 2 } // GetSpiConnection returns an spi connection to a device on a specified bus. // Valid bus number is [0..1] which corresponds to /dev/spidev0.0 through /dev/spidev0.1. -func (b *Adaptor) GetSpiConnection(busNum, chipNum, mode, bits int, maxSpeed int64) (connection spi.Connection, err error) { - b.mutex.Lock() - defer b.mutex.Unlock() +func (c *Adaptor) GetSpiConnection(busNum, chipNum, mode, bits int, maxSpeed int64) (connection spi.Connection, err error) { + c.mutex.Lock() + defer c.mutex.Unlock() if (busNum < 0) || (busNum > 1) { return nil, fmt.Errorf("Bus number %d out of range", busNum) } - if b.spiBuses[busNum] == nil { - b.spiBuses[busNum], err = spi.GetSpiConnection(busNum, chipNum, mode, bits, maxSpeed) + if c.spiBuses[busNum] == nil { + c.spiBuses[busNum], err = spi.GetSpiConnection(busNum, chipNum, mode, bits, maxSpeed) } - return b.spiBuses[busNum], err + return c.spiBuses[busNum], err } // GetSpiDefaultBus returns the default spi bus for this platform. -func (b *Adaptor) GetSpiDefaultBus() int { +func (c *Adaptor) GetSpiDefaultBus() int { return 0 } // GetSpiDefaultChip returns the default spi chip for this platform. -func (b *Adaptor) GetSpiDefaultChip() int { +func (c *Adaptor) GetSpiDefaultChip() int { return 0 } // GetSpiDefaultMode returns the default spi mode for this platform. -func (b *Adaptor) GetSpiDefaultMode() int { +func (c *Adaptor) GetSpiDefaultMode() int { return 0 } // GetSpiDefaultBits returns the default spi number of bits for this platform. -func (b *Adaptor) GetSpiDefaultBits() int { +func (c *Adaptor) GetSpiDefaultBits() int { return 8 } // GetSpiDefaultMaxSpeed returns the default spi bus for this platform. -func (b *Adaptor) GetSpiDefaultMaxSpeed() int64 { +func (c *Adaptor) GetSpiDefaultMaxSpeed() int64 { return 500000 } // translatePin converts digital pin name to pin position -func (b *Adaptor) translateDigitalPin(pin string) (value int, err error) { - if val, ok := b.pinMap[pin]; ok { - value = val - } else { - err = errors.New("Not a valid pin") +func (c *Adaptor) translateAndMuxDigitalPin(id string) (string, int, error) { + line, ok := c.pinMap[id] + if !ok { + return "", -1, fmt.Errorf("'%s' is not a valid id for a digital pin", id) } - return + // mux is done by id, not by line + if err := c.muxPin(id, "gpio"); err != nil { + return "", -1, err + } + return "", line, nil } -func (b *Adaptor) translatePwmPin(pin string) (p pwmPinData, err error) { - if val, ok := b.pwmPinMap[pin]; ok { - p = val - } else { - err = errors.New("Not a valid PWM pin") +func (c *Adaptor) translatePwmPin(pin string) (pwmPinData, error) { + if val, ok := c.pwmPinMap[pin]; ok { + return val, nil } - return + return pwmPinData{}, errors.New("Not a valid PWM pin") } // translateAnalogPin converts analog pin name to pin position -func (b *Adaptor) translateAnalogPin(pin string) (value string, err error) { - if val, ok := b.analogPinMap[pin]; ok { - value = val - } else { - err = errors.New("Not a valid analog pin") +func (c *Adaptor) translateAnalogPin(pin string) (string, error) { + if val, ok := c.analogPinMap[pin]; ok { + return val, nil } - return + return "", errors.New("Not a valid analog pin") } -func (b *Adaptor) digitalPin(id string, o ...func(gobot.DigitalPinOptioner) bool) (gobot.DigitalPinner, error) { - pin := b.digitalPins[id] - if pin == nil { - i, err := b.translateDigitalPin(id) - if err != nil { - return nil, err - } - pin = b.sys.NewDigitalPin("", i, o...) - if err := b.muxPin(id, "gpio"); err != nil { - return nil, err - } - if err := pin.Export(); err != nil { - return nil, err - } - b.digitalPins[id] = pin - } else { - if err := pin.ApplyOptions(o...); err != nil { - return nil, err - } - } - - return pin, nil -} - -func (b *Adaptor) muxPin(pin, cmd string) error { +func (c *Adaptor) muxPin(pin, cmd string) error { path := fmt.Sprintf("/sys/devices/platform/ocp/ocp:%s_pinmux/state", pin) - fi, e := b.sys.OpenFile(path, os.O_WRONLY, 0666) + fi, e := c.sys.OpenFile(path, os.O_WRONLY, 0666) defer fi.Close() if e != nil { return e diff --git a/platforms/beaglebone/beaglebone_adaptor_test.go b/platforms/beaglebone/beaglebone_adaptor_test.go index ab4bec1c..c0202e09 100644 --- a/platforms/beaglebone/beaglebone_adaptor_test.go +++ b/platforms/beaglebone/beaglebone_adaptor_test.go @@ -44,20 +44,16 @@ func initTestAdaptorWithMockedFilesystem(mockPaths []string) (*Adaptor, *system. return pinPath, nil } } - + if err := a.Connect(); err != nil { + panic(err) + } return a, fs } -func TestGeneral(t *testing.T) { +func TestPWM(t *testing.T) { mockPaths := []string{ - "/dev/i2c-2", - "/sys/devices/platform/bone_capemgr", - "/sys/devices/platform/ocp/ocp:P8_07_pinmux/state", - "/sys/devices/platform/ocp/ocp:P9_11_pinmux/state", - "/sys/devices/platform/ocp/ocp:P9_12_pinmux/state", "/sys/devices/platform/ocp/ocp:P9_22_pinmux/state", "/sys/devices/platform/ocp/ocp:P9_21_pinmux/state", - "/sys/class/leds/beaglebone:green:usr1/brightness", "/sys/bus/iio/devices/iio:device0/in_voltage1_raw", "/sys/devices/platform/ocp/48300000.epwmss/48300200.pwm/pwm/pwmchip0/export", "/sys/devices/platform/ocp/48300000.epwmss/48300200.pwm/pwm/pwmchip0/unexport", @@ -69,21 +65,10 @@ func TestGeneral(t *testing.T) { "/sys/devices/platform/ocp/48300000.epwmss/48300200.pwm/pwm/pwmchip0/pwm1/period", "/sys/devices/platform/ocp/48300000.epwmss/48300200.pwm/pwm/pwmchip0/pwm1/duty_cycle", "/sys/devices/platform/ocp/48300000.epwmss/48300200.pwm/pwm/pwmchip0/pwm1/polarity", - "/sys/class/gpio/export", - "/sys/class/gpio/unexport", - "/sys/class/gpio/gpio60/value", - "/sys/class/gpio/gpio60/direction", - "/sys/class/gpio/gpio66/value", - "/sys/class/gpio/gpio66/direction", - "/sys/class/gpio/gpio10/value", - "/sys/class/gpio/gpio10/direction", - "/sys/class/gpio/gpio30/value", - "/sys/class/gpio/gpio30/direction", } a, fs := initTestAdaptorWithMockedFilesystem(mockPaths) - // PWM gobottest.Assert(t, a.PwmWrite("P9_99", 175), errors.New("Not a valid PWM pin")) a.PwmWrite("P9_21", 175) gobottest.Assert( @@ -118,7 +103,16 @@ func TestGeneral(t *testing.T) { gobottest.Assert(t, strings.Contains(a.PwmWrite("P9_22", 175).Error(), "write error"), true) fs.WithWriteError = false - // Analog + gobottest.Assert(t, a.Finalize(), nil) +} + +func TestAnalog(t *testing.T) { + mockPaths := []string{ + "/sys/bus/iio/devices/iio:device0/in_voltage1_raw", + } + + a, fs := initTestAdaptorWithMockedFilesystem(mockPaths) + fs.Files["/sys/bus/iio/devices/iio:device0/in_voltage1_raw"].Contents = "567\n" i, err := a.AnalogRead("P9_40") gobottest.Assert(t, i, 567) @@ -132,6 +126,29 @@ func TestGeneral(t *testing.T) { gobottest.Assert(t, err, errors.New("read error")) fs.WithReadError = false + gobottest.Assert(t, a.Finalize(), nil) +} + +func TestDigitalIO(t *testing.T) { + mockPaths := []string{ + "/sys/devices/platform/ocp/ocp:P8_07_pinmux/state", + "/sys/devices/platform/ocp/ocp:P9_11_pinmux/state", + "/sys/devices/platform/ocp/ocp:P9_12_pinmux/state", + "/sys/class/leds/beaglebone:green:usr1/brightness", + "/sys/class/gpio/export", + "/sys/class/gpio/unexport", + "/sys/class/gpio/gpio60/value", + "/sys/class/gpio/gpio60/direction", + "/sys/class/gpio/gpio66/value", + "/sys/class/gpio/gpio66/direction", + "/sys/class/gpio/gpio10/value", + "/sys/class/gpio/gpio10/direction", + "/sys/class/gpio/gpio30/value", + "/sys/class/gpio/gpio30/direction", + } + + a, fs := initTestAdaptorWithMockedFilesystem(mockPaths) + // DigitalIO a.DigitalWrite("usr1", 1) gobottest.Assert(t, @@ -140,33 +157,28 @@ func TestGeneral(t *testing.T) { ) // no such LED - err = a.DigitalWrite("usr10101", 1) + err := a.DigitalWrite("usr10101", 1) gobottest.Assert(t, err.Error(), " : /sys/class/leds/beaglebone:green:usr10101/brightness: No such file.") a.DigitalWrite("P9_12", 1) gobottest.Assert(t, fs.Files["/sys/class/gpio/gpio60/value"].Contents, "1") - gobottest.Assert(t, a.DigitalWrite("P9_99", 1), errors.New("Not a valid pin")) + gobottest.Assert(t, a.DigitalWrite("P9_99", 1), errors.New("'P9_99' is not a valid id for a digital pin")) _, err = a.DigitalRead("P9_99") - gobottest.Assert(t, err, errors.New("Not a valid pin")) + gobottest.Assert(t, err, errors.New("'P9_99' is not a valid id for a digital pin")) fs.Files["/sys/class/gpio/gpio66/value"].Contents = "1" - i, err = a.DigitalRead("P8_07") + i, err := a.DigitalRead("P8_07") gobottest.Assert(t, i, 1) gobottest.Assert(t, err, nil) - fs.WithReadError = true - _, err = a.DigitalRead("P8_07") - gobottest.Assert(t, err, errors.New("read error")) - fs.WithReadError = false + gobottest.Assert(t, a.Finalize(), nil) +} - fs.WithWriteError = true - _, err = a.DigitalRead("P9_11") - gobottest.Assert(t, err, errors.New("write error")) - fs.WithWriteError = false +func TestI2c(t *testing.T) { + a, _ := initTestAdaptorWithMockedFilesystem([]string{"/dev/i2c-2"}) - // I2c a.sys.UseMockSyscall() con, err := a.GetConnection(0xff, 2) diff --git a/platforms/chip/chip_adaptor.go b/platforms/chip/chip_adaptor.go index 195112f1..664f428f 100644 --- a/platforms/chip/chip_adaptor.go +++ b/platforms/chip/chip_adaptor.go @@ -12,9 +12,13 @@ import ( multierror "github.com/hashicorp/go-multierror" "gobot.io/x/gobot" "gobot.io/x/gobot/drivers/i2c" + "gobot.io/x/gobot/platforms/adaptors" "gobot.io/x/gobot/system" ) +// Valids pins are the XIO-P0 through XIO-P7 pins from the +// extender (pins 13-20 on header 14), as well as the SoC pins +// aka all the other pins. type sysfsPin struct { pin int pwmPin int @@ -22,37 +26,40 @@ type sysfsPin struct { // Adaptor represents a Gobot Adaptor for a C.H.I.P. type Adaptor struct { - name string - board string - sys *system.Accesser - mutex sync.Mutex - pinmap map[string]sysfsPin - digitalPins map[int]gobot.DigitalPinner - pwmPins map[int]gobot.PWMPinner - i2cBuses [3]i2c.I2cDevice + name string + sys *system.Accesser + mutex sync.Mutex + pinmap map[string]sysfsPin + *adaptors.DigitalPinsAdaptor + pwmPins map[int]gobot.PWMPinner + i2cBuses [3]i2c.I2cDevice } // NewAdaptor creates a C.H.I.P. Adaptor func NewAdaptor() *Adaptor { + sys := system.NewAccesser() c := &Adaptor{ - name: gobot.DefaultName("CHIP"), - board: "chip", - sys: system.NewAccesser(), + name: gobot.DefaultName("CHIP"), + sys: sys, } - c.setPins() + c.pwmPins = make(map[int]gobot.PWMPinner) + c.pinmap = chipPins + baseAddr, _ := getXIOBase() + for i := 0; i < 8; i++ { + pin := fmt.Sprintf("XIO-P%d", i) + c.pinmap[pin] = sysfsPin{pin: baseAddr + i, pwmPin: -1} + } + + c.DigitalPinsAdaptor = adaptors.NewDigitalPinsAdaptor(sys, c.translateDigitalPin) return c } // NewProAdaptor creates a C.H.I.P. Pro Adaptor func NewProAdaptor() *Adaptor { - c := &Adaptor{ - name: gobot.DefaultName("CHIP Pro"), - sys: system.NewAccesser(), - board: "pro", - } - - c.setPins() + c := NewAdaptor() + c.name = gobot.DefaultName("CHIP Pro") + c.pinmap = chipProPins return c } @@ -62,23 +69,22 @@ func (c *Adaptor) Name() string { return c.name } // SetName sets the name of the Adaptor func (c *Adaptor) SetName(n string) { c.name = n } -// Connect initializes the board -func (c *Adaptor) Connect() (err error) { - return nil -} - -// Finalize closes connection to board and pins -func (c *Adaptor) Finalize() (err error) { +// Connect create new connection to board and pins. +func (c *Adaptor) Connect() error { c.mutex.Lock() defer c.mutex.Unlock() - for _, pin := range c.digitalPins { - if pin != nil { - if e := pin.Unexport(); e != nil { - err = multierror.Append(err, e) - } - } - } + err := c.DigitalPinsAdaptor.Connect() + return err +} + +// Finalize closes connection to board and pins +func (c *Adaptor) Finalize() error { + c.mutex.Lock() + defer c.mutex.Unlock() + + err := c.DigitalPinsAdaptor.Finalize() + for _, pin := range c.pwmPins { if pin != nil { if errs := pin.Enable(false); errs != nil { @@ -96,46 +102,7 @@ func (c *Adaptor) Finalize() (err error) { } } } - return -} - -// DigitalRead reads digital value from the specified pin. -// Valids pins are the XIO-P0 through XIO-P7 pins from the -// extender (pins 13-20 on header 14), as well as the SoC pins -// aka all the other pins. -func (c *Adaptor) DigitalRead(id string) (int, error) { - c.mutex.Lock() - defer c.mutex.Unlock() - - pin, err := c.digitalPin(id, system.WithDirectionInput()) - if err != nil { - return 0, err - } - return pin.Read() -} - -// DigitalWrite writes digital value to the specified pin. -// Valids pins are the XIO-P0 through XIO-P7 pins from the -// extender (pins 13-20 on header 14), as well as the SoC pins -// aka all the other pins. -func (c *Adaptor) DigitalWrite(id string, val byte) error { - c.mutex.Lock() - defer c.mutex.Unlock() - - pin, err := c.digitalPin(id, system.WithDirectionOutput(int(val))) - if err != nil { - return err - } - return pin.Write(int(val)) -} - -// DigitalPin returns a digital pin. If the pin is initially acquired, it is an input. -// Pin direction and other options can be changed afterwards by pin.ApplyOptions() at any time. -func (c *Adaptor) DigitalPin(id string) (gobot.DigitalPinner, error) { - c.mutex.Lock() - defer c.mutex.Unlock() - - return c.digitalPin(id) + return err } // GetConnection returns a connection to a device on a specified bus. @@ -224,33 +191,6 @@ func (c *Adaptor) ServoWrite(pin string, angle byte) (err error) { return pwmPin.SetDutyCycle(duty) } -// SetBoard sets the name of the type of board -func (c *Adaptor) SetBoard(n string) (err error) { - if n == "chip" || n == "pro" { - c.board = n - c.setPins() - return - } - return errors.New("Invalid board type") -} - -func (c *Adaptor) setPins() { - c.digitalPins = make(map[int]gobot.DigitalPinner) - c.pwmPins = make(map[int]gobot.PWMPinner) - - if c.board == "pro" { - c.pinmap = chipProPins - return - } - // otherwise, original CHIP - c.pinmap = chipPins - baseAddr, _ := getXIOBase() - for i := 0; i < 8; i++ { - pin := fmt.Sprintf("XIO-P%d", i) - c.pinmap[pin] = sysfsPin{pin: baseAddr + i, pwmPin: -1} - } -} - func getXIOBase() (baseAddr int, err error) { // Default to original base from 4.3 kernel baseAddr = 408 @@ -281,34 +221,9 @@ func getXIOBase() (baseAddr int, err error) { return baseAddr, nil } -func (c *Adaptor) translateDigitalPin(id string) (i int, err error) { +func (c *Adaptor) translateDigitalPin(id string) (string, int, error) { if val, ok := c.pinmap[id]; ok { - i = val.pin - } else { - err = errors.New("Not a valid pin") + return "", val.pin, nil } - return -} - -func (c *Adaptor) digitalPin(id string, o ...func(gobot.DigitalPinOptioner) bool) (gobot.DigitalPinner, error) { - i, err := c.translateDigitalPin(id) - if err != nil { - return nil, err - } - - pin := c.digitalPins[i] - - if pin == nil { - pin = c.sys.NewDigitalPin("", i, o...) - if err = pin.Export(); err != nil { - return nil, err - } - c.digitalPins[i] = pin - } else { - if err := pin.ApplyOptions(o...); err != nil { - return nil, err - } - } - - return pin, nil + return "", -1, fmt.Errorf("'%s' is not a valid id for a digital pin", id) } diff --git a/platforms/chip/chip_adaptor_test.go b/platforms/chip/chip_adaptor_test.go index e2635500..9667965d 100644 --- a/platforms/chip/chip_adaptor_test.go +++ b/platforms/chip/chip_adaptor_test.go @@ -40,12 +40,18 @@ var mockPaths = []string{ func initTestAdaptorWithMockedFilesystem() (*Adaptor, *system.MockFilesystem) { a := NewAdaptor() fs := a.sys.UseMockFilesystem(mockPaths) + if err := a.Connect(); err != nil { + panic(err) + } return a, fs } func initTestProAdaptorWithMockedFilesystem() (*Adaptor, *system.MockFilesystem) { a := NewProAdaptor() fs := a.sys.UseMockFilesystem(mockPaths) + if err := a.Connect(); err != nil { + panic(err) + } return a, fs } @@ -56,12 +62,9 @@ func TestName(t *testing.T) { gobottest.Assert(t, a.Name(), "NewName") } -func TestNewAdaptor(t *testing.T) { - a := NewAdaptor() - a.SetBoard("pro") - gobottest.Assert(t, a.board, "pro") - - gobottest.Assert(t, a.SetBoard("bad"), errors.New("Invalid board type")) +func TestNewProAdaptor(t *testing.T) { + a := NewProAdaptor() + gobottest.Assert(t, strings.HasPrefix(a.Name(), "CHIP Pro"), true) } func TestFinalizeErrorAfterGPIO(t *testing.T) { @@ -97,7 +100,7 @@ func TestDigitalIO(t *testing.T) { i, _ := a.DigitalRead("TWI2-SDA") gobottest.Assert(t, i, 1) - gobottest.Assert(t, a.DigitalWrite("XIO-P10", 1), errors.New("Not a valid pin")) + gobottest.Assert(t, a.DigitalWrite("XIO-P10", 1), errors.New("'XIO-P10' is not a valid id for a digital pin")) gobottest.Assert(t, a.Finalize(), nil) } @@ -112,26 +115,10 @@ func TestProDigitalIO(t *testing.T) { i, _ := a.DigitalRead("TWI2-SDA") gobottest.Assert(t, i, 1) - gobottest.Assert(t, a.DigitalWrite("XIO-P0", 1), errors.New("Not a valid pin")) + gobottest.Assert(t, a.DigitalWrite("XIO-P0", 1), errors.New("'XIO-P0' is not a valid id for a digital pin")) gobottest.Assert(t, a.Finalize(), nil) } -func TestDigitalWriteError(t *testing.T) { - a, fs := initTestAdaptorWithMockedFilesystem() - fs.WithWriteError = true - - err := a.DigitalWrite("CSID7", 1) - gobottest.Assert(t, err, errors.New("write error")) -} - -func TestDigitalReadWriteError(t *testing.T) { - a, fs := initTestAdaptorWithMockedFilesystem() - fs.WithWriteError = true - - _, err := a.DigitalRead("CSID7") - gobottest.Assert(t, err, errors.New("write error")) -} - func TestI2c(t *testing.T) { a := NewAdaptor() a.sys.UseMockFilesystem([]string{"/dev/i2c-1"}) diff --git a/platforms/dragonboard/dragonboard_adaptor.go b/platforms/dragonboard/dragonboard_adaptor.go index 282af61e..04e8c490 100644 --- a/platforms/dragonboard/dragonboard_adaptor.go +++ b/platforms/dragonboard/dragonboard_adaptor.go @@ -1,26 +1,29 @@ package dragonboard import ( - "errors" "fmt" "sync" multierror "github.com/hashicorp/go-multierror" "gobot.io/x/gobot" "gobot.io/x/gobot/drivers/i2c" + "gobot.io/x/gobot/platforms/adaptors" "gobot.io/x/gobot/system" ) // Adaptor represents a Gobot Adaptor for a DragonBoard 410c type Adaptor struct { - name string - sys *system.Accesser - mutex sync.Mutex - digitalPins map[int]gobot.DigitalPinner - pinMap map[string]int - i2cBuses [3]i2c.I2cDevice + name string + sys *system.Accesser + mutex sync.Mutex + *adaptors.DigitalPinsAdaptor + pinMap map[string]int + i2cBuses [3]i2c.I2cDevice } +// Valid pins are the GPIO_A through GPIO_L pins from the +// extender (pins 23-34 on header J8), as well as the SoC pins +// aka all the other pins, APQ GPIO_0-GPIO_122 and PM_MPP_0-4. var fixedPins = map[string]int{ "GPIO_A": 36, "GPIO_B": 12, @@ -41,12 +44,17 @@ var fixedPins = map[string]int{ // NewAdaptor creates a DragonBoard 410c Adaptor func NewAdaptor() *Adaptor { + sys := system.NewAccesser() c := &Adaptor{ name: gobot.DefaultName("DragonBoard"), - sys: system.NewAccesser(), + sys: sys, + } + c.DigitalPinsAdaptor = adaptors.NewDigitalPinsAdaptor(sys, c.translateDigitalPin) + c.pinMap = fixedPins + for i := 0; i < 122; i++ { + pin := fmt.Sprintf("GPIO_%d", i) + c.pinMap[pin] = i } - - c.setPins() return c } @@ -56,23 +64,22 @@ func (c *Adaptor) Name() string { return c.name } // SetName sets the name of the Adaptor func (c *Adaptor) SetName(n string) { c.name = n } -// Connect initializes the board -func (c *Adaptor) Connect() (err error) { - return -} - -// Finalize closes connection to board and pins -func (c *Adaptor) Finalize() (err error) { +// Connect create new connection to board and pins. +func (c *Adaptor) Connect() error { c.mutex.Lock() defer c.mutex.Unlock() - for _, pin := range c.digitalPins { - if pin != nil { - if e := pin.Unexport(); e != nil { - err = multierror.Append(err, e) - } - } - } + err := c.DigitalPinsAdaptor.Connect() + return err +} + +// Finalize closes connection to board and pins +func (c *Adaptor) Finalize() error { + c.mutex.Lock() + defer c.mutex.Unlock() + + err := c.DigitalPinsAdaptor.Finalize() + for _, bus := range c.i2cBuses { if bus != nil { if e := bus.Close(); e != nil { @@ -80,46 +87,7 @@ func (c *Adaptor) Finalize() (err error) { } } } - return -} - -// DigitalRead reads digital value to the specified pin. -// Valids pins are the GPIO_A through GPIO_L pins from the -// extender (pins 23-34 on header J8), as well as the SoC pins -// aka all the other pins, APQ GPIO_0-GPIO_122 and PM_MPP_0-4. -func (c *Adaptor) DigitalRead(id string) (int, error) { - c.mutex.Lock() - defer c.mutex.Unlock() - - pin, err := c.digitalPin(id, system.WithDirectionInput()) - if err != nil { - return 0, err - } - return pin.Read() -} - -// DigitalWrite writes digital value to the specified pin. -// Valids pins are the GPIO_A through GPIO_L pins from the -// extender (pins 23-34 on header J8), as well as the SoC pins -// aka all the other pins, APQ GPIO_0-GPIO_122 and PM_MPP_0-4. -func (c *Adaptor) DigitalWrite(id string, val byte) error { - c.mutex.Lock() - defer c.mutex.Unlock() - - pin, err := c.digitalPin(id, system.WithDirectionOutput(int(val))) - if err != nil { - return err - } - return pin.Write(int(val)) -} - -// DigitalPin returns a digital pin. If the pin is initially acquired, it is an input. -// Pin direction and other options can be changed afterwards by pin.ApplyOptions() at any time. -func (c *Adaptor) DigitalPin(id string) (gobot.DigitalPinner, error) { - c.mutex.Lock() - defer c.mutex.Unlock() - - return c.digitalPin(id) + return err } // GetConnection returns a connection to a device on a specified bus. @@ -142,42 +110,9 @@ func (c *Adaptor) GetDefaultBus() int { return 0 } -func (c *Adaptor) setPins() { - c.digitalPins = make(map[int]gobot.DigitalPinner) - c.pinMap = fixedPins - for i := 0; i < 122; i++ { - pin := fmt.Sprintf("GPIO_%d", i) - c.pinMap[pin] = i +func (c *Adaptor) translateDigitalPin(id string) (string, int, error) { + if line, ok := c.pinMap[id]; ok { + return "", line, nil } -} - -func (c *Adaptor) translateDigitalPin(id string) (i int, err error) { - if val, ok := c.pinMap[id]; ok { - i = val - } else { - err = errors.New("Not a valid pin") - } - return -} - -func (c *Adaptor) digitalPin(id string, o ...func(gobot.DigitalPinOptioner) bool) (gobot.DigitalPinner, error) { - i, err := c.translateDigitalPin(id) - if err != nil { - return nil, err - } - - pin := c.digitalPins[i] - if pin == nil { - pin = c.sys.NewDigitalPin("", i, o...) - if err = pin.Export(); err != nil { - return nil, err - } - c.digitalPins[i] = pin - } else { - if err := pin.ApplyOptions(o...); err != nil { - return nil, err - } - } - - return pin, nil + return "", -1, fmt.Errorf("'%s' is not a valid id for a digital pin", id) } diff --git a/platforms/dragonboard/dragonboard_adaptor_test.go b/platforms/dragonboard/dragonboard_adaptor_test.go index a6b90f9b..1c1b49e6 100644 --- a/platforms/dragonboard/dragonboard_adaptor_test.go +++ b/platforms/dragonboard/dragonboard_adaptor_test.go @@ -13,6 +13,7 @@ import ( // make sure that this Adaptor fulfills all the required interfaces var _ gobot.Adaptor = (*Adaptor)(nil) +var _ gobot.DigitalPinnerProvider = (*Adaptor)(nil) var _ gpio.DigitalReader = (*Adaptor)(nil) var _ gpio.DigitalWriter = (*Adaptor)(nil) var _ i2c.Connector = (*Adaptor)(nil) @@ -20,7 +21,7 @@ var _ i2c.Connector = (*Adaptor)(nil) func initTestAdaptor(t *testing.T) *Adaptor { a := NewAdaptor() if err := a.Connect(); err != nil { - t.Error(err) + panic(err) } return a } @@ -51,7 +52,7 @@ func TestDigitalIO(t *testing.T) { i, _ := a.DigitalRead("GPIO_A") gobottest.Assert(t, i, 1) - gobottest.Assert(t, a.DigitalWrite("GPIO_M", 1), errors.New("Not a valid pin")) + gobottest.Assert(t, a.DigitalWrite("GPIO_M", 1), errors.New("'GPIO_M' is not a valid id for a digital pin")) gobottest.Assert(t, a.Finalize(), nil) } diff --git a/platforms/intel-iot/joule/joule_adaptor.go b/platforms/intel-iot/joule/joule_adaptor.go index 5faab1e1..f302899d 100644 --- a/platforms/intel-iot/joule/joule_adaptor.go +++ b/platforms/intel-iot/joule/joule_adaptor.go @@ -8,6 +8,7 @@ import ( multierror "github.com/hashicorp/go-multierror" "gobot.io/x/gobot" "gobot.io/x/gobot/drivers/i2c" + "gobot.io/x/gobot/platforms/adaptors" "gobot.io/x/gobot/system" ) @@ -18,51 +19,48 @@ type sysfsPin struct { // Adaptor represents an Intel Joule type Adaptor struct { - name string - sys *system.Accesser - mutex sync.Mutex - digitalPins map[int]gobot.DigitalPinner - pwmPins map[int]gobot.PWMPinner - i2cBuses [3]i2c.I2cDevice + name string + sys *system.Accesser + mutex sync.Mutex + *adaptors.DigitalPinsAdaptor + pwmPins map[int]gobot.PWMPinner + i2cBuses [3]i2c.I2cDevice } // NewAdaptor returns a new Joule Adaptor func NewAdaptor() *Adaptor { - return &Adaptor{ + sys := system.NewAccesser() + c := &Adaptor{ name: gobot.DefaultName("Joule"), - sys: system.NewAccesser(), + sys: sys, } + c.DigitalPinsAdaptor = adaptors.NewDigitalPinsAdaptor(sys, c.translateDigitalPin) + return c } // Name returns the Adaptors name -func (e *Adaptor) Name() string { return e.name } +func (c *Adaptor) Name() string { return c.name } // SetName sets the Adaptors name -func (e *Adaptor) SetName(n string) { e.name = n } +func (c *Adaptor) SetName(n string) { c.name = n } -// Connect initializes the Joule for use with the Arduino beakout board -func (e *Adaptor) Connect() (err error) { - e.mutex.Lock() - defer e.mutex.Unlock() +// Connect initializes the Joule for use with the Arduino breakout board +func (c *Adaptor) Connect() error { + c.mutex.Lock() + defer c.mutex.Unlock() - e.digitalPins = make(map[int]gobot.DigitalPinner) - e.pwmPins = make(map[int]gobot.PWMPinner) - return + c.pwmPins = make(map[int]gobot.PWMPinner) + return c.DigitalPinsAdaptor.Connect() } // Finalize releases all i2c devices and exported digital and pwm pins. -func (e *Adaptor) Finalize() (err error) { - e.mutex.Lock() - defer e.mutex.Unlock() +func (c *Adaptor) Finalize() error { + c.mutex.Lock() + defer c.mutex.Unlock() - for _, pin := range e.digitalPins { - if pin != nil { - if errs := pin.Unexport(); errs != nil { - err = multierror.Append(err, errs) - } - } - } - for _, pin := range e.pwmPins { + err := c.DigitalPinsAdaptor.Finalize() + + for _, pin := range c.pwmPins { if pin != nil { if errs := pin.Enable(false); errs != nil { err = multierror.Append(err, errs) @@ -72,53 +70,19 @@ func (e *Adaptor) Finalize() (err error) { } } } - for _, bus := range e.i2cBuses { + for _, bus := range c.i2cBuses { if bus != nil { if errs := bus.Close(); errs != nil { err = multierror.Append(err, errs) } } } - return -} - -// DigitalRead reads digital value from pin -func (e *Adaptor) DigitalRead(id string) (int, error) { - e.mutex.Lock() - defer e.mutex.Unlock() - - pin, err := e.digitalPin(id, system.WithDirectionInput()) - if err != nil { - return 0, err - } - return pin.Read() -} - -// DigitalWrite writes a value to the pin. Acceptable values are 1 or 0. -func (e *Adaptor) DigitalWrite(id string, val byte) error { - e.mutex.Lock() - defer e.mutex.Unlock() - - pin, err := e.digitalPin(id, system.WithDirectionOutput(int(val))) - if err != nil { - return err - } - - return pin.Write(int(val)) -} - -// DigitalPin returns a digital pin. If the pin is initially acquired, it is an input. -// Pin direction and other options can be changed afterwards by pin.ApplyOptions() at any time. -func (e *Adaptor) DigitalPin(id string) (gobot.DigitalPinner, error) { - e.mutex.Lock() - defer e.mutex.Unlock() - - return e.digitalPin(id) + return err } // PwmWrite writes the 0-254 value to the specified pin -func (e *Adaptor) PwmWrite(pin string, val byte) (err error) { - pwmPin, err := e.PWMPin(pin) +func (c *Adaptor) PwmWrite(pin string, val byte) (err error) { + pwmPin, err := c.PWMPin(pin) if err != nil { return } @@ -130,30 +94,30 @@ func (e *Adaptor) PwmWrite(pin string, val byte) (err error) { return pwmPin.SetDutyCycle(uint32(float64(period) * duty)) } -// PWMPin returns a sys.PWMPin -func (e *Adaptor) PWMPin(pin string) (gobot.PWMPinner, error) { - e.mutex.Lock() - defer e.mutex.Unlock() +// PWMPin returns a PWM pin, implements the interface gobot.PWMPinnerProvider +func (c *Adaptor) PWMPin(pin string) (gobot.PWMPinner, error) { + c.mutex.Lock() + defer c.mutex.Unlock() sysPin, ok := sysfsPinMap[pin] if !ok { return nil, errors.New("Not a valid pin") } if sysPin.pwmPin != -1 { - if e.pwmPins[sysPin.pwmPin] == nil { - e.pwmPins[sysPin.pwmPin] = e.sys.NewPWMPin("/sys/class/pwm/pwmchip0", sysPin.pwmPin) - if err := e.pwmPins[sysPin.pwmPin].Export(); err != nil { + if c.pwmPins[sysPin.pwmPin] == nil { + c.pwmPins[sysPin.pwmPin] = c.sys.NewPWMPin("/sys/class/pwm/pwmchip0", sysPin.pwmPin) + if err := c.pwmPins[sysPin.pwmPin].Export(); err != nil { return nil, err } - if err := e.pwmPins[sysPin.pwmPin].SetPeriod(10000000); err != nil { + if err := c.pwmPins[sysPin.pwmPin].SetPeriod(10000000); err != nil { return nil, err } - if err := e.pwmPins[sysPin.pwmPin].Enable(true); err != nil { + if err := c.pwmPins[sysPin.pwmPin].Enable(true); err != nil { return nil, err } } - return e.pwmPins[sysPin.pwmPin], nil + return c.pwmPins[sysPin.pwmPin], nil } return nil, errors.New("Not a PWM pin") @@ -161,35 +125,24 @@ func (e *Adaptor) PWMPin(pin string) (gobot.PWMPinner, error) { // GetConnection returns an i2c connection to a device on a specified bus. // Valid bus number is [0..2] which corresponds to /dev/i2c-0 through /dev/i2c-2. -func (e *Adaptor) GetConnection(address int, bus int) (connection i2c.Connection, err error) { +func (c *Adaptor) GetConnection(address int, bus int) (connection i2c.Connection, err error) { if (bus < 0) || (bus > 2) { return nil, fmt.Errorf("Bus number %d out of range", bus) } - if e.i2cBuses[bus] == nil { - e.i2cBuses[bus], err = e.sys.NewI2cDevice(fmt.Sprintf("/dev/i2c-%d", bus)) + if c.i2cBuses[bus] == nil { + c.i2cBuses[bus], err = c.sys.NewI2cDevice(fmt.Sprintf("/dev/i2c-%d", bus)) } - return i2c.NewConnection(e.i2cBuses[bus], address), err + return i2c.NewConnection(c.i2cBuses[bus], address), err } // GetDefaultBus returns the default i2c bus for this platform -func (e *Adaptor) GetDefaultBus() int { +func (c *Adaptor) GetDefaultBus() int { return 0 } -func (e *Adaptor) digitalPin(id string, o ...func(gobot.DigitalPinOptioner) bool) (gobot.DigitalPinner, error) { - i := sysfsPinMap[id] - pin := e.digitalPins[i.pin] - - if pin == nil { - pin = e.sys.NewDigitalPin("", i.pin, o...) - if err := pin.Export(); err != nil { - return nil, err - } - e.digitalPins[i.pin] = pin - } else { - if err := pin.ApplyOptions(o...); err != nil { - return nil, err - } +func (c *Adaptor) translateDigitalPin(id string) (string, int, error) { + if val, ok := sysfsPinMap[id]; ok { + return "", val.pin, nil } - return pin, nil + return "", -1, fmt.Errorf("'%s' is not a valid id for a digital pin", id) } diff --git a/platforms/intel-iot/joule/joule_adaptor_test.go b/platforms/intel-iot/joule/joule_adaptor_test.go index 7f4836d9..3d365a0d 100644 --- a/platforms/intel-iot/joule/joule_adaptor_test.go +++ b/platforms/intel-iot/joule/joule_adaptor_test.go @@ -2,7 +2,6 @@ package joule import ( "errors" - "log" "strings" "testing" @@ -134,33 +133,17 @@ func TestFinalize(t *testing.T) { func TestDigitalIO(t *testing.T) { a, fs := initTestAdaptorWithMockedFilesystem() - log.Println("now test write") a.DigitalWrite("J12_1", 1) gobottest.Assert(t, fs.Files["/sys/class/gpio/gpio451/value"].Contents, "1") - log.Println("now test write") a.DigitalWrite("J12_1", 0) - log.Println("now test read") i, err := a.DigitalRead("J12_1") gobottest.Assert(t, err, nil) gobottest.Assert(t, i, 0) -} -func TestDigitalWriteError(t *testing.T) { - a, fs := initTestAdaptorWithMockedFilesystem() - fs.WithWriteError = true - - err := a.DigitalWrite("13", 1) - gobottest.Assert(t, err, errors.New("write error")) -} - -func TestDigitalReadWriteError(t *testing.T) { - a, fs := initTestAdaptorWithMockedFilesystem() - fs.WithWriteError = true - - _, err := a.DigitalRead("13") - gobottest.Assert(t, err, errors.New("write error")) + _, err = a.DigitalRead("P9_99") + gobottest.Assert(t, err, errors.New("'P9_99' is not a valid id for a digital pin")) } func TestI2c(t *testing.T) { diff --git a/platforms/jetson/jetson_adaptor.go b/platforms/jetson/jetson_adaptor.go index 721eb473..b9b37d38 100644 --- a/platforms/jetson/jetson_adaptor.go +++ b/platforms/jetson/jetson_adaptor.go @@ -9,58 +9,68 @@ import ( "gobot.io/x/gobot" "gobot.io/x/gobot/drivers/i2c" "gobot.io/x/gobot/drivers/spi" + "gobot.io/x/gobot/platforms/adaptors" "gobot.io/x/gobot/system" ) const pwmDefaultPeriod = 3000000 -// Adaptor is the Gobot Adaptor for the Jetson Nano +// Adaptor is the Gobot adaptor for the Jetson Nano type Adaptor struct { - name string - sys *system.Accesser - mutex sync.Mutex - digitalPins map[int]gobot.DigitalPinner - pwmPins map[int]gobot.PWMPinner - i2cBuses [2]i2c.I2cDevice - spiDevices [2]spi.Connection + name string + sys *system.Accesser + mutex sync.Mutex + *adaptors.DigitalPinsAdaptor + pwmPins map[string]gobot.PWMPinner + i2cBuses [2]i2c.I2cDevice + spiDevices [2]spi.Connection } -// NewAdaptor creates a Raspi Adaptor +// NewAdaptor creates a Jetson Nano adaptor func NewAdaptor() *Adaptor { - j := &Adaptor{ - name: gobot.DefaultName("JetsonNano"), - sys: system.NewAccesser(), - digitalPins: make(map[int]gobot.DigitalPinner), - pwmPins: make(map[int]gobot.PWMPinner), + sys := system.NewAccesser() + c := &Adaptor{ + name: gobot.DefaultName("JetsonNano"), + sys: sys, + pwmPins: make(map[string]gobot.PWMPinner), } - return j + c.DigitalPinsAdaptor = adaptors.NewDigitalPinsAdaptor(sys, c.translateDigitalPin) + return c } // Name returns the Adaptor's name -func (j *Adaptor) Name() string { - j.mutex.Lock() - defer j.mutex.Unlock() +func (c *Adaptor) Name() string { + c.mutex.Lock() + defer c.mutex.Unlock() - return j.name + return c.name } // SetName sets the Adaptor's name -func (j *Adaptor) SetName(n string) { - j.mutex.Lock() - defer j.mutex.Unlock() +func (c *Adaptor) SetName(n string) { + c.mutex.Lock() + defer c.mutex.Unlock() - j.name = n + c.name = n } -// Connect do nothing at the moment -func (j *Adaptor) Connect() error { return nil } +// Connect create new connection to board and pins. +func (c *Adaptor) Connect() error { + c.mutex.Lock() + defer c.mutex.Unlock() + + err := c.DigitalPinsAdaptor.Connect() + return err +} // Finalize closes connection to board and pins -func (j *Adaptor) Finalize() (err error) { - j.mutex.Lock() - defer j.mutex.Unlock() +func (c *Adaptor) Finalize() error { + c.mutex.Lock() + defer c.mutex.Unlock() - for _, pin := range j.digitalPins { + err := c.DigitalPinsAdaptor.Finalize() + + for _, pin := range c.pwmPins { if pin != nil { if perr := pin.Unexport(); err != nil { err = multierror.Append(err, perr) @@ -68,150 +78,107 @@ func (j *Adaptor) Finalize() (err error) { } } - for _, pin := range j.pwmPins { - if pin != nil { - if perr := pin.Unexport(); err != nil { - err = multierror.Append(err, perr) - } - } - } - - for _, bus := range j.i2cBuses { + for _, bus := range c.i2cBuses { if bus != nil { if e := bus.Close(); e != nil { err = multierror.Append(err, e) } } } - for _, dev := range j.spiDevices { + for _, dev := range c.spiDevices { if dev != nil { if e := dev.Close(); e != nil { err = multierror.Append(err, e) } } } - return -} - -// DigitalRead reads digital value from pin -func (j *Adaptor) DigitalRead(id string) (int, error) { - j.mutex.Lock() - defer j.mutex.Unlock() - - pin, err := j.digitalPin(id, system.WithDirectionInput()) - if err != nil { - return 0, err - } - return pin.Read() -} - -// DigitalWrite writes digital value to specified pin -func (j *Adaptor) DigitalWrite(id string, val byte) error { - j.mutex.Lock() - defer j.mutex.Unlock() - - pin, err := j.digitalPin(id, system.WithDirectionOutput(int(val))) - if err != nil { - return err - } - return pin.Write(int(val)) -} - -// DigitalPin returns a digital pin. If the pin is initially acquired, it is an input. -// Pin direction and other options can be changed afterwards by pin.ApplyOptions() at any time. -func (j *Adaptor) DigitalPin(id string) (gobot.DigitalPinner, error) { - j.mutex.Lock() - defer j.mutex.Unlock() - - return j.digitalPin(id) + return err } // GetConnection returns an i2c connection to a device on a specified bus. // Valid bus number is [0..1] which corresponds to /dev/i2c-0 through /dev/i2c-1. -func (j *Adaptor) GetConnection(address int, bus int) (connection i2c.Connection, err error) { +func (c *Adaptor) GetConnection(address int, bus int) (connection i2c.Connection, err error) { if (bus < 0) || (bus > 1) { return nil, fmt.Errorf("Bus number %d out of range", bus) } - device, err := j.getI2cBus(bus) + device, err := c.getI2cBus(bus) return i2c.NewConnection(device, address), err } // GetDefaultBus returns the default i2c bus for this platform -func (j *Adaptor) GetDefaultBus() int { +func (c *Adaptor) GetDefaultBus() int { return 1 } // GetSpiConnection returns an spi connection to a device on a specified bus. // Valid bus number is [0..1] which corresponds to /dev/spidev0.0 through /dev/spidev0.1. -func (j *Adaptor) GetSpiConnection(busNum, chipNum, mode, bits int, maxSpeed int64) (connection spi.Connection, err error) { - j.mutex.Lock() - defer j.mutex.Unlock() +func (c *Adaptor) GetSpiConnection(busNum, chipNum, mode, bits int, maxSpeed int64) (connection spi.Connection, err error) { + c.mutex.Lock() + defer c.mutex.Unlock() if (busNum < 0) || (busNum > 1) { return nil, fmt.Errorf("Bus number %d out of range", busNum) } - if j.spiDevices[busNum] == nil { - j.spiDevices[busNum], err = spi.GetSpiConnection(busNum, chipNum, mode, bits, maxSpeed) + if c.spiDevices[busNum] == nil { + c.spiDevices[busNum], err = spi.GetSpiConnection(busNum, chipNum, mode, bits, maxSpeed) } - return j.spiDevices[busNum], err + return c.spiDevices[busNum], err } // GetSpiDefaultBus returns the default spi bus for this platform. -func (j *Adaptor) GetSpiDefaultBus() int { +func (c *Adaptor) GetSpiDefaultBus() int { return 0 } // GetSpiDefaultChip returns the default spi chip for this platform. -func (j *Adaptor) GetSpiDefaultChip() int { +func (c *Adaptor) GetSpiDefaultChip() int { return 0 } // GetSpiDefaultMode returns the default spi mode for this platform. -func (j *Adaptor) GetSpiDefaultMode() int { +func (c *Adaptor) GetSpiDefaultMode() int { return 0 } // GetSpiDefaultBits returns the default spi number of bits for this platform. -func (j *Adaptor) GetSpiDefaultBits() int { +func (c *Adaptor) GetSpiDefaultBits() int { return 8 } // GetSpiDefaultMaxSpeed returns the default spi bus for this platform. -func (j *Adaptor) GetSpiDefaultMaxSpeed() int64 { +func (c *Adaptor) GetSpiDefaultMaxSpeed() int64 { return 10000000 } //PWMPin returns a Jetson Nano. PWMPin which provides the gobot.PWMPinner interface -func (j *Adaptor) PWMPin(pin string) (gobot.PWMPinner, error) { - i, err := j.translatePin(pin) +func (c *Adaptor) PWMPin(pin string) (gobot.PWMPinner, error) { + c.mutex.Lock() + defer c.mutex.Unlock() + + if c.pwmPins[pin] != nil { + return c.pwmPins[pin], nil + } + + fn, err := c.translatePwmPin(pin) if err != nil { return nil, err } - j.mutex.Lock() - defer j.mutex.Unlock() - if j.pwmPins[i] != nil { - return j.pwmPins[i], nil - } + c.pwmPins[pin] = NewPWMPin(c.sys, "/sys/class/pwm/pwmchip0", fn) + c.pwmPins[pin].Export() + c.pwmPins[pin].SetPeriod(pwmDefaultPeriod) + c.pwmPins[pin].Enable(true) - j.pwmPins[i], err = NewPWMPin(j.sys, "/sys/class/pwm/pwmchip0", pin) - if err != nil { - return nil, err - } - j.pwmPins[i].Export() - j.pwmPins[i].SetPeriod(pwmDefaultPeriod) - j.pwmPins[i].Enable(true) - - return j.pwmPins[i], nil + return c.pwmPins[pin], nil } // PwmWrite writes a PWM signal to the specified pin -func (j *Adaptor) PwmWrite(pin string, val byte) (err error) { - sysPin, err := j.PWMPin(pin) +func (c *Adaptor) PwmWrite(pin string, val byte) (err error) { + sysPin, err := c.PWMPin(pin) if err != nil { return err } @@ -221,8 +188,8 @@ func (j *Adaptor) PwmWrite(pin string, val byte) (err error) { } // ServoWrite writes a servo signal to the specified pin -func (j *Adaptor) ServoWrite(pin string, angle byte) (err error) { - sysPin, err := j.PWMPin(pin) +func (c *Adaptor) ServoWrite(pin string, angle byte) (err error) { + sysPin, err := c.PWMPin(pin) if err != nil { return err } @@ -231,45 +198,27 @@ func (j *Adaptor) ServoWrite(pin string, angle byte) (err error) { return sysPin.SetDutyCycle(duty) } -func (j *Adaptor) getI2cBus(bus int) (_ i2c.I2cDevice, err error) { - j.mutex.Lock() - defer j.mutex.Unlock() +func (c *Adaptor) getI2cBus(bus int) (_ i2c.I2cDevice, err error) { + c.mutex.Lock() + defer c.mutex.Unlock() - if j.i2cBuses[bus] == nil { - j.i2cBuses[bus], err = j.sys.NewI2cDevice(fmt.Sprintf("/dev/i2c-%d", bus)) + if c.i2cBuses[bus] == nil { + c.i2cBuses[bus], err = c.sys.NewI2cDevice(fmt.Sprintf("/dev/i2c-%d", bus)) } - return j.i2cBuses[bus], err + return c.i2cBuses[bus], err } -func (j *Adaptor) translatePin(pin string) (i int, err error) { - if val, ok := pins[pin]["*"]; ok { - i = val - } else { - err = errors.New("Not a valid pin") - return +func (c *Adaptor) translateDigitalPin(id string) (string, int, error) { + if line, ok := gpioPins[id]; ok { + return "", line, nil } - return + return "", -1, fmt.Errorf("'%s' is not a valid id for a digital pin", id) } -func (j *Adaptor) digitalPin(id string, o ...func(gobot.DigitalPinOptioner) bool) (gobot.DigitalPinner, error) { - i, err := j.translatePin(id) - if err != nil { - return nil, err +func (c *Adaptor) translatePwmPin(pin string) (fn string, err error) { + if fn, ok := pwmPins[pin]; ok { + return fn, nil } - - pin := j.digitalPins[i] - if pin == nil { - pin = j.sys.NewDigitalPin("", i, o...) - if err := pin.Export(); err != nil { - return nil, err - } - j.digitalPins[i] = pin - } else { - if err := pin.ApplyOptions(o...); err != nil { - return nil, err - } - } - - return pin, nil + return "", errors.New("Not a valid pin") } diff --git a/platforms/jetson/jetson_adaptor_test.go b/platforms/jetson/jetson_adaptor_test.go index 1a75dd15..93202a82 100644 --- a/platforms/jetson/jetson_adaptor_test.go +++ b/platforms/jetson/jetson_adaptor_test.go @@ -29,6 +29,9 @@ var _ spi.Connector = (*Adaptor)(nil) func initTestAdaptorWithMockedFilesystem(mockPaths []string) (*Adaptor, *system.MockFilesystem) { a := NewAdaptor() fs := a.sys.UseMockFilesystem(mockPaths) + if err := a.Connect(); err != nil { + panic(err) + } return a, fs } @@ -80,15 +83,8 @@ func TestDigitalIO(t *testing.T) { gobottest.Assert(t, err, nil) gobottest.Assert(t, i, 1) - gobottest.Assert(t, a.DigitalWrite("notexist", 1), errors.New("Not a valid pin")) - - fs.WithReadError = true - _, err = a.DigitalRead("13") - gobottest.Assert(t, err, errors.New("read error")) - - fs.WithWriteError = true - _, err = a.DigitalRead("7") - gobottest.Assert(t, err, errors.New("write error")) + gobottest.Assert(t, a.DigitalWrite("notexist", 1), errors.New("'notexist' is not a valid id for a digital pin")) + gobottest.Assert(t, a.Finalize(), nil) } func TestI2c(t *testing.T) { diff --git a/platforms/jetson/jetson_pin_map.go b/platforms/jetson/jetson_pin_map.go index 84da5ccd..7aae07d8 100644 --- a/platforms/jetson/jetson_pin_map.go +++ b/platforms/jetson/jetson_pin_map.go @@ -1,75 +1,31 @@ package jetson -var pins = map[string]map[string]int{ - "7": { - "*": 216, - }, - "11": { - "*": 50, - }, - "12": { - "*": 79, - }, - "13": { - "*": 14, - }, - "15": { - "*": 194, - }, - "16": { - "*": 232, - }, - "18": { - "*": 15, - }, - "19": { - "*": 16, - }, - "21": { - "*": 17, - }, - "22": { - "*": 13, - }, - "23": { - "*": 18, - }, - "24": { - "*": 19, - }, - "26": { - "*": 20, - }, - "29": { - "*": 149, - }, - "31": { - "*": 200, - }, - "32": { - "*": 168, - }, - "33": { - "*": 38, - }, - "35": { - "*": 76, - }, - "36": { - "*": 51, - }, - "37": { - "*": 12, - }, - "38": { - "*": 77, - }, - "40": { - "*": 78, - }, +var gpioPins = map[string]int{ + "7": 216, + "11": 50, + "12": 79, + "13": 14, + "15": 194, + "16": 232, + "18": 15, + "19": 16, + "21": 17, + "22": 13, + "23": 18, + "24": 19, + "26": 20, + "29": 149, + "31": 200, + "32": 168, + "33": 38, + "35": 76, + "36": 51, + "37": 12, + "38": 77, + "40": 78, } -var pwms = map[string]string{ +var pwmPins = map[string]string{ "32": "0", "33": "2", } diff --git a/platforms/jetson/pwm_pin.go b/platforms/jetson/pwm_pin.go index 51fe0906..aee9408b 100644 --- a/platforms/jetson/pwm_pin.go +++ b/platforms/jetson/pwm_pin.go @@ -20,7 +20,6 @@ const ( type PWMPin struct { sys *system.Accesser path string - pin string fn string dc uint32 period uint32 @@ -28,19 +27,13 @@ type PWMPin struct { // NewPWMPin returns a new PWMPin // pin32 pwm0, pin33 pwm2 -func NewPWMPin(sys *system.Accesser, path string, pin string) (p *PWMPin, err error) { - if val, ok := pwms[pin]; ok { - p = &PWMPin{ - sys: sys, - path: path, - pin: pin, - fn: val, - } - } else { - err = errors.New("Not a valid pin") +func NewPWMPin(sys *system.Accesser, path string, fn string) *PWMPin { + p := &PWMPin{ + sys: sys, + path: path, + fn: fn, } - - return + return p } // Export exports the pin for use by the Jetson Nano diff --git a/platforms/jetson/pwm_pin_test.go b/platforms/jetson/pwm_pin_test.go index a1cc27ee..ef135ac8 100644 --- a/platforms/jetson/pwm_pin_test.go +++ b/platforms/jetson/pwm_pin_test.go @@ -19,13 +19,10 @@ func TestPwmPin(t *testing.T) { "/sys/class/pwm/pwmchip0/pwm0/enable", "/sys/class/pwm/pwmchip0/pwm0/period", "/sys/class/pwm/pwmchip0/pwm0/duty_cycle", - "/sys/class/pwm/pwmchip0/pwm2/enable", - "/sys/class/pwm/pwmchip0/pwm2/period", - "/sys/class/pwm/pwmchip0/pwm2/duty_cycle", } a.UseMockFilesystem(mockPaths) - pin, err := NewPWMPin(a, "/sys/class/pwm/pwmchip0", "32") + pin := NewPWMPin(a, "/sys/class/pwm/pwmchip0", "0") gobottest.Assert(t, pin.Export(), nil) gobottest.Assert(t, pin.Enable(true), nil) val, _ := pin.Polarity() diff --git a/platforms/raspi/raspi_adaptor_test.go b/platforms/raspi/raspi_adaptor_test.go index a001c109..f3cc0948 100644 --- a/platforms/raspi/raspi_adaptor_test.go +++ b/platforms/raspi/raspi_adaptor_test.go @@ -161,14 +161,7 @@ func TestDigitalIO(t *testing.T) { gobottest.Assert(t, i, 1) gobottest.Assert(t, a.DigitalWrite("notexist", 1), errors.New("Not a valid pin")) - - fs.WithReadError = true - _, err = a.DigitalRead("13") - gobottest.Assert(t, err, errors.New("read error")) - - fs.WithWriteError = true - _, err = a.DigitalRead("7") - gobottest.Assert(t, err, errors.New("write error")) + gobottest.Assert(t, a.Finalize(), nil) } func TestI2c(t *testing.T) { diff --git a/platforms/tinkerboard/adaptor.go b/platforms/tinkerboard/adaptor.go index a663f650..50e5dc90 100644 --- a/platforms/tinkerboard/adaptor.go +++ b/platforms/tinkerboard/adaptor.go @@ -8,6 +8,7 @@ import ( multierror "github.com/hashicorp/go-multierror" "gobot.io/x/gobot" "gobot.io/x/gobot/drivers/i2c" + "gobot.io/x/gobot/platforms/adaptors" "gobot.io/x/gobot/system" ) @@ -27,22 +28,23 @@ type pwmPinDefinition struct { // Adaptor represents a Gobot Adaptor for the ASUS Tinker Board type Adaptor struct { - name string - sys *system.Accesser - mutex sync.Mutex - digitalPins map[string]gobot.DigitalPinner - pwmPins map[string]gobot.PWMPinner - i2cBuses [5]i2c.I2cDevice + name string + sys *system.Accesser + mutex sync.Mutex + *adaptors.DigitalPinsAdaptor + pwmPins map[string]gobot.PWMPinner + i2cBuses [5]i2c.I2cDevice } // NewAdaptor creates a Tinkerboard Adaptor func NewAdaptor() *Adaptor { + sys := system.NewAccesser("cdev") c := &Adaptor{ - name: gobot.DefaultName("Tinker Board"), - sys: system.NewAccesser("cdev"), + name: gobot.DefaultName("Tinker Board"), + sys: sys, + pwmPins: make(map[string]gobot.PWMPinner), } - - c.setPins() + c.DigitalPinsAdaptor = adaptors.NewDigitalPinsAdaptor(sys, c.translateDigitalPin) return c } @@ -52,21 +54,22 @@ func (c *Adaptor) Name() string { return c.name } // SetName sets the name of the Adaptor func (c *Adaptor) SetName(n string) { c.name = n } -// Connect do nothing at the moment -func (c *Adaptor) Connect() error { return nil } - -// Finalize closes connection to board and pins -func (c *Adaptor) Finalize() (err error) { +// Connect create new connection to board and pins. +func (c *Adaptor) Connect() error { c.mutex.Lock() defer c.mutex.Unlock() - for _, pin := range c.digitalPins { - if pin != nil { - if e := pin.Unexport(); e != nil { - err = multierror.Append(err, e) - } - } - } + err := c.DigitalPinsAdaptor.Connect() + return err +} + +// Finalize closes connection to board and pins +func (c *Adaptor) Finalize() error { + c.mutex.Lock() + defer c.mutex.Unlock() + + err := c.DigitalPinsAdaptor.Finalize() + for _, pin := range c.pwmPins { if pin != nil { if errs := pin.Enable(false); errs != nil { @@ -84,40 +87,7 @@ func (c *Adaptor) Finalize() (err error) { } } } - return -} - -// DigitalRead reads digital value from the specified pin. -func (c *Adaptor) DigitalRead(id string) (int, error) { - c.mutex.Lock() - defer c.mutex.Unlock() - - pin, err := c.digitalPin(id, system.WithDirectionInput()) - if err != nil { - return 0, err - } - return pin.Read() -} - -// DigitalWrite writes digital value to the specified pin. -func (c *Adaptor) DigitalWrite(id string, val byte) error { - c.mutex.Lock() - defer c.mutex.Unlock() - - pin, err := c.digitalPin(id, system.WithDirectionOutput(int(val))) - if err != nil { - return err - } - return pin.Write(int(val)) -} - -// DigitalPin returns a digital pin. If the pin is initially acquired, it is an input. -// Pin direction and other options can be changed afterwards by pin.ApplyOptions() at any time. -func (c *Adaptor) DigitalPin(id string) (gobot.DigitalPinner, error) { - c.mutex.Lock() - defer c.mutex.Unlock() - - return c.digitalPin(id) + return err } // PwmWrite writes a PWM signal to the specified pin. @@ -216,7 +186,7 @@ func (c *Adaptor) pwmPin(pin string) (gobot.PWMPinner, error) { if c.pwmPins[pin] == nil { var path string - path, err := pwmPinData.findDir(*c.sys) + path, err := pwmPinData.findPwmDir(*c.sys) if err != nil { return nil, err } @@ -296,22 +266,16 @@ func setPeriod(pwmPin gobot.PWMPinner, period uint32) error { return nil } -func (c *Adaptor) setPins() { - c.digitalPins = make(map[string]gobot.DigitalPinner) - c.pwmPins = make(map[string]gobot.PWMPinner) -} - -func (c *Adaptor) translatePin(pin string) (chip string, line int, err error) { - pindef, ok := gpioPinDefinitions[pin] +func (c *Adaptor) translateDigitalPin(id string) (string, int, error) { + pindef, ok := gpioPinDefinitions[id] if !ok { - return "", -1, fmt.Errorf("Not a valid pin") + return "", -1, fmt.Errorf("'%s' is not a valid id for a digital pin", id) } - if c.sys.IsSysfsDigitalPinAccess() { return "", pindef.sysfs, nil } - chip = fmt.Sprintf("gpiochip%d", pindef.cdev.chip) - line = int(pindef.cdev.line) + chip := fmt.Sprintf("gpiochip%d", pindef.cdev.chip) + line := int(pindef.cdev.line) return chip, line, nil } @@ -323,7 +287,7 @@ func (c *Adaptor) translatePwmPin(pin string) (pwmPin pwmPinDefinition, err erro return } -func (p pwmPinDefinition) findDir(sys system.Accesser) (dir string, err error) { +func (p pwmPinDefinition) findPwmDir(sys system.Accesser) (dir string, err error) { items, _ := sys.Find(p.dir, p.dirRegexp) if items == nil || len(items) == 0 { return "", fmt.Errorf("No path found for PWM directory pattern, '%s' in path '%s'. See README.md for activation", p.dirRegexp, p.dir) @@ -340,25 +304,3 @@ func (p pwmPinDefinition) findDir(sys system.Accesser) (dir string, err error) { return } - -func (c *Adaptor) digitalPin(id string, o ...func(gobot.DigitalPinOptioner) bool) (gobot.DigitalPinner, error) { - chip, line, err := c.translatePin(id) - if err != nil { - return nil, err - } - - pin := c.digitalPins[id] - if pin == nil { - pin = c.sys.NewDigitalPin(chip, line, o...) - if err = pin.Export(); err != nil { - return nil, err - } - c.digitalPins[id] = pin - } else { - if err := pin.ApplyOptions(o...); err != nil { - return nil, err - } - } - - return pin, nil -} diff --git a/platforms/tinkerboard/adaptor_test.go b/platforms/tinkerboard/adaptor_test.go index df6cf764..1bd49d61 100644 --- a/platforms/tinkerboard/adaptor_test.go +++ b/platforms/tinkerboard/adaptor_test.go @@ -67,6 +67,9 @@ func preparePwmFs(fs *system.MockFilesystem) { func initTestAdaptorWithMockedFilesystem(mockPaths []string) (*Adaptor, *system.MockFilesystem) { a := NewAdaptor() fs := a.sys.UseMockFilesystem(mockPaths) + if err := a.Connect(); err != nil { + panic(err) + } return a, fs } @@ -77,7 +80,50 @@ func TestName(t *testing.T) { gobottest.Assert(t, a.Name(), "NewName") } +func Test_translateDigitalPin(t *testing.T) { + var tests = map[string]struct { + access string + pin string + wantChip string + wantLine int + wantErr error + }{ + "cdev_ok": { + access: "cdev", + pin: "7", + wantChip: "gpiochip0", + wantLine: 17, + }, + "sysfs_ok": { + access: "sysfs", + pin: "7", + wantChip: "", + wantLine: 17, + }, + "unknown_pin": { + pin: "99", + wantChip: "", + wantLine: -1, + wantErr: fmt.Errorf("'99' is not a valid id for a digital pin"), + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + // arrange + a := NewAdaptor() + a.sys.UseDigitalPinAccessWithMockFs(tc.access, []string{}) + // act + chip, line, err := a.translateDigitalPin(tc.pin) + // assert + gobottest.Assert(t, err, tc.wantErr) + gobottest.Assert(t, chip, tc.wantChip) + gobottest.Assert(t, line, tc.wantLine) + }) + } +} + func TestDigitalIO(t *testing.T) { + // only basic tests needed, further tests are done in "digitalpinsadaptor.go" a, fs := initTestAdaptorWithMockedFilesystem(gpioMockPaths) a.DigitalWrite("7", 1) @@ -87,26 +133,10 @@ func TestDigitalIO(t *testing.T) { i, _ := a.DigitalRead("10") gobottest.Assert(t, i, 1) - gobottest.Assert(t, a.DigitalWrite("99", 1), errors.New("Not a valid pin")) + gobottest.Assert(t, a.DigitalWrite("99", 1), errors.New("'99' is not a valid id for a digital pin")) gobottest.Assert(t, a.Finalize(), nil) } -func TestDigitalWriteError(t *testing.T) { - a, fs := initTestAdaptorWithMockedFilesystem(gpioMockPaths) - fs.WithWriteError = true - - err := a.DigitalWrite("7", 1) - gobottest.Assert(t, err, errors.New("write error")) -} - -func TestDigitalReadWriteError(t *testing.T) { - a, fs := initTestAdaptorWithMockedFilesystem(gpioMockPaths) - fs.WithWriteError = true - - _, err := a.DigitalRead("7") - gobottest.Assert(t, err, errors.New("write error")) -} - func TestInvalidPWMPin(t *testing.T) { a, fs := initTestAdaptorWithMockedFilesystem(pwmMockPaths) preparePwmFs(fs) diff --git a/platforms/upboard/up2/adaptor.go b/platforms/upboard/up2/adaptor.go index 129a04a1..9b21d06a 100644 --- a/platforms/upboard/up2/adaptor.go +++ b/platforms/upboard/up2/adaptor.go @@ -11,6 +11,7 @@ import ( "gobot.io/x/gobot" "gobot.io/x/gobot/drivers/i2c" "gobot.io/x/gobot/drivers/spi" + "gobot.io/x/gobot/platforms/adaptors" "gobot.io/x/gobot/system" ) @@ -38,27 +39,28 @@ type sysfsPin struct { // Adaptor represents a Gobot Adaptor for the Upboard UP2 type Adaptor struct { - name string - sys *system.Accesser - mutex sync.Mutex - pinmap map[string]sysfsPin - ledPath string - digitalPins map[int]gobot.DigitalPinner - pwmPins map[int]gobot.PWMPinner - i2cBuses [6]i2c.I2cDevice - spiBuses [2]spi.Connection + name string + sys *system.Accesser + mutex sync.Mutex + pinmap map[string]sysfsPin + ledPath string + *adaptors.DigitalPinsAdaptor + pwmPins map[int]gobot.PWMPinner + i2cBuses [6]i2c.I2cDevice + spiBuses [2]spi.Connection } // NewAdaptor creates a UP2 Adaptor func NewAdaptor() *Adaptor { + sys := system.NewAccesser() c := &Adaptor{ - name: gobot.DefaultName("UP2"), - sys: system.NewAccesser(), - ledPath: "/sys/class/leds/upboard:%s:/brightness", - digitalPins: make(map[int]gobot.DigitalPinner), - pwmPins: make(map[int]gobot.PWMPinner), - pinmap: fixedPins, + name: gobot.DefaultName("UP2"), + sys: sys, + ledPath: "/sys/class/leds/upboard:%s:/brightness", + pwmPins: make(map[int]gobot.PWMPinner), + pinmap: fixedPins, } + c.DigitalPinsAdaptor = adaptors.NewDigitalPinsAdaptor(sys, c.translateDigitalPin) return c } @@ -68,21 +70,22 @@ func (c *Adaptor) Name() string { return c.name } // SetName sets the name of the Adaptor func (c *Adaptor) SetName(n string) { c.name = n } -// Connect do nothing at the moment -func (c *Adaptor) Connect() error { return nil } - -// Finalize closes connection to board and pins -func (c *Adaptor) Finalize() (err error) { +// Connect create new connection to board and pins. +func (c *Adaptor) Connect() error { c.mutex.Lock() defer c.mutex.Unlock() - for _, pin := range c.digitalPins { - if pin != nil { - if e := pin.Unexport(); e != nil { - err = multierror.Append(err, e) - } - } - } + err := c.DigitalPinsAdaptor.Connect() + return err +} + +// Finalize closes connection to board and pins +func (c *Adaptor) Finalize() error { + c.mutex.Lock() + defer c.mutex.Unlock() + + err := c.DigitalPinsAdaptor.Finalize() + for _, pin := range c.pwmPins { if pin != nil { if errs := pin.Enable(false); errs != nil { @@ -108,19 +111,7 @@ func (c *Adaptor) Finalize() (err error) { } } - return -} - -// DigitalRead reads digital value from the specified pin. -func (c *Adaptor) DigitalRead(id string) (int, error) { - c.mutex.Lock() - defer c.mutex.Unlock() - - pin, err := c.digitalPin(id, system.WithDirectionInput()) - if err != nil { - return 0, err - } - return pin.Read() + return err } // DigitalWrite writes digital value to the specified pin. @@ -131,29 +122,16 @@ func (c *Adaptor) DigitalWrite(id string, val byte) error { // is it one of the built-in LEDs? if id == LEDRed || id == LEDBlue || id == LEDGreen || id == LEDYellow { pinPath := fmt.Sprintf(c.ledPath, id) - fi, e := c.sys.OpenFile(pinPath, os.O_WRONLY|os.O_APPEND, 0666) + fi, err := c.sys.OpenFile(pinPath, os.O_WRONLY|os.O_APPEND, 0666) defer fi.Close() - if e != nil { - return e + if err != nil { + return err } - _, err := fi.WriteString(strconv.Itoa(int(val))) + _, err = fi.WriteString(strconv.Itoa(int(val))) return err } - pin, err := c.digitalPin(id, system.WithDirectionOutput(int(val))) - if err != nil { - return err - } - return pin.Write(int(val)) -} - -// DigitalPin returns a digital pin. If the pin is initially acquired, it is an input. -// Pin direction and other options can be changed afterwards by pin.ApplyOptions() at any time. -func (c *Adaptor) DigitalPin(id string) (gobot.DigitalPinner, error) { - c.mutex.Lock() - defer c.mutex.Unlock() - - return c.digitalPin(id) + return c.DigitalPinsAdaptor.DigitalWrite(id, val) } // PwmWrite writes a PWM signal to the specified pin @@ -186,28 +164,6 @@ func (c *Adaptor) ServoWrite(pin string, angle byte) (err error) { return pwmPin.SetDutyCycle(duty) } -func (c *Adaptor) digitalPin(id string, o ...func(gobot.DigitalPinOptioner) bool) (gobot.DigitalPinner, error) { - i, err := c.translatePin(id) - if err != nil { - return nil, err - } - - pin := c.digitalPins[i] - if pin == nil { - pin = c.sys.NewDigitalPin("", i, o...) - if err = pin.Export(); err != nil { - return nil, err - } - c.digitalPins[i] = pin - } else { - if err := pin.ApplyOptions(o...); err != nil { - return nil, err - } - } - - return pin, nil -} - // PWMPin returns matched pwmPin for specified pin number func (c *Adaptor) PWMPin(pin string) (gobot.PWMPinner, error) { c.mutex.Lock() @@ -307,13 +263,11 @@ func (c *Adaptor) GetSpiDefaultMaxSpeed() int64 { return 500000 } -func (c *Adaptor) translatePin(pin string) (i int, err error) { - if val, ok := c.pinmap[pin]; ok { - i = val.pin - } else { - err = errors.New("Not a valid pin") +func (c *Adaptor) translateDigitalPin(id string) (string, int, error) { + if val, ok := c.pinmap[id]; ok { + return "", val.pin, nil } - return + return "", -1, fmt.Errorf("'%s' is not a valid id for a digital pin", id) } func (c *Adaptor) translatePwmPin(pin string) (i int, err error) { diff --git a/platforms/upboard/up2/adaptor_test.go b/platforms/upboard/up2/adaptor_test.go index a3428a44..adb9970e 100644 --- a/platforms/upboard/up2/adaptor_test.go +++ b/platforms/upboard/up2/adaptor_test.go @@ -24,24 +24,31 @@ var _ gpio.ServoWriter = (*Adaptor)(nil) var _ i2c.Connector = (*Adaptor)(nil) var _ spi.Connector = (*Adaptor)(nil) -func initTestAdaptorWithMockedFilesystem() (*Adaptor, *system.MockFilesystem) { +var pwmMockPaths = []string{ + "/sys/class/pwm/pwmchip0/export", + "/sys/class/pwm/pwmchip0/unexport", + "/sys/class/pwm/pwmchip0/pwm0/enable", + "/sys/class/pwm/pwmchip0/pwm0/period", + "/sys/class/pwm/pwmchip0/pwm0/duty_cycle", + "/sys/class/pwm/pwmchip0/pwm0/polarity", +} + +var gpioMockPaths = []string{ + "/sys/class/gpio/export", + "/sys/class/gpio/unexport", + "/sys/class/gpio/gpio462/value", + "/sys/class/gpio/gpio462/direction", + "/sys/class/gpio/gpio432/value", + "/sys/class/gpio/gpio432/direction", + "/sys/class/leds/upboard:green:/brightness", +} + +func initTestAdaptorWithMockedFilesystem(mockPaths []string) (*Adaptor, *system.MockFilesystem) { a := NewAdaptor() - mockPaths := []string{ - "/sys/class/gpio/export", - "/sys/class/gpio/unexport", - "/sys/class/gpio/gpio462/value", - "/sys/class/gpio/gpio462/direction", - "/sys/class/gpio/gpio432/value", - "/sys/class/gpio/gpio432/direction", - "/sys/class/pwm/pwmchip0/export", - "/sys/class/pwm/pwmchip0/unexport", - "/sys/class/pwm/pwmchip0/pwm0/enable", - "/sys/class/pwm/pwmchip0/pwm0/period", - "/sys/class/pwm/pwmchip0/pwm0/duty_cycle", - "/sys/class/pwm/pwmchip0/pwm0/polarity", - "/sys/class/leds/upboard:green:/brightness", - } fs := a.sys.UseMockFilesystem(mockPaths) + if err := a.Connect(); err != nil { + panic(err) + } return a, fs } @@ -53,7 +60,7 @@ func TestName(t *testing.T) { } func TestDigitalIO(t *testing.T) { - a, fs := initTestAdaptorWithMockedFilesystem() + a, fs := initTestAdaptorWithMockedFilesystem(gpioMockPaths) a.DigitalWrite("7", 1) gobottest.Assert(t, fs.Files["/sys/class/gpio/gpio462/value"].Contents, "1") @@ -68,26 +75,10 @@ func TestDigitalIO(t *testing.T) { "1", ) - gobottest.Assert(t, a.DigitalWrite("99", 1), errors.New("Not a valid pin")) + gobottest.Assert(t, a.DigitalWrite("99", 1), errors.New("'99' is not a valid id for a digital pin")) gobottest.Assert(t, a.Finalize(), nil) } -func TestDigitalWriteError(t *testing.T) { - a, fs := initTestAdaptorWithMockedFilesystem() - fs.WithWriteError = true - - err := a.DigitalWrite("7", 1) - gobottest.Assert(t, err, errors.New("write error")) -} - -func TestDigitalReadWriteError(t *testing.T) { - a, fs := initTestAdaptorWithMockedFilesystem() - fs.WithWriteError = true - - _, err := a.DigitalRead("7") - gobottest.Assert(t, err, errors.New("write error")) -} - func TestI2c(t *testing.T) { a := NewAdaptor() a.sys.UseMockFilesystem([]string{"/dev/i2c-5"}) @@ -122,7 +113,7 @@ func TestSPI(t *testing.T) { } func TestInvalidPWMPin(t *testing.T) { - a, _ := initTestAdaptorWithMockedFilesystem() + a, _ := initTestAdaptorWithMockedFilesystem(pwmMockPaths) err := a.PwmWrite("666", 42) gobottest.Assert(t, err.Error(), "Not a valid pin") @@ -138,7 +129,7 @@ func TestInvalidPWMPin(t *testing.T) { } func TestPWM(t *testing.T) { - a, fs := initTestAdaptorWithMockedFilesystem() + a, fs := initTestAdaptorWithMockedFilesystem(pwmMockPaths) err := a.PwmWrite("32", 100) gobottest.Assert(t, err, nil) @@ -161,7 +152,7 @@ func TestPWM(t *testing.T) { } func TestPwmWriteError(t *testing.T) { - a, fs := initTestAdaptorWithMockedFilesystem() + a, fs := initTestAdaptorWithMockedFilesystem(pwmMockPaths) fs.WithWriteError = true err := a.PwmWrite("32", 100) @@ -169,7 +160,7 @@ func TestPwmWriteError(t *testing.T) { } func TestPwmReadError(t *testing.T) { - a, fs := initTestAdaptorWithMockedFilesystem() + a, fs := initTestAdaptorWithMockedFilesystem(pwmMockPaths) fs.WithReadError = true err := a.PwmWrite("32", 100) @@ -177,19 +168,19 @@ func TestPwmReadError(t *testing.T) { } func TestI2CDefaultBus(t *testing.T) { - a, _ := initTestAdaptorWithMockedFilesystem() + a := NewAdaptor() gobottest.Assert(t, a.GetDefaultBus(), 5) } func TestGetConnectionInvalidBus(t *testing.T) { - a, _ := initTestAdaptorWithMockedFilesystem() + a := NewAdaptor() _, err := a.GetConnection(0x01, 99) gobottest.Assert(t, err, errors.New("Bus number 99 out of range")) } func TestFinalizeErrorAfterGPIO(t *testing.T) { - a, fs := initTestAdaptorWithMockedFilesystem() + a, fs := initTestAdaptorWithMockedFilesystem(gpioMockPaths) gobottest.Assert(t, a.DigitalWrite("7", 1), nil) @@ -200,7 +191,7 @@ func TestFinalizeErrorAfterGPIO(t *testing.T) { } func TestFinalizeErrorAfterPWM(t *testing.T) { - a, fs := initTestAdaptorWithMockedFilesystem() + a, fs := initTestAdaptorWithMockedFilesystem(pwmMockPaths) gobottest.Assert(t, a.PwmWrite("32", 1), nil) diff --git a/system/digitalpin_mock.go b/system/digitalpin_mock.go index a5752a35..e22c5b27 100644 --- a/system/digitalpin_mock.go +++ b/system/digitalpin_mock.go @@ -4,21 +4,21 @@ import ( "gobot.io/x/gobot" ) -type mockDigitalPinHandler struct { +type mockDigitalPinAccess struct { fs *MockFilesystem } type digitalPinMock struct{} -func (h *mockDigitalPinHandler) isSupported() bool { return true } +func (h *mockDigitalPinAccess) isSupported() bool { return true } -func (h *mockDigitalPinHandler) createPin(chip string, pin int, +func (h *mockDigitalPinAccess) createPin(chip string, pin int, o ...func(gobot.DigitalPinOptioner) bool) gobot.DigitalPinner { dpm := &digitalPinMock{} return dpm } -func (h *mockDigitalPinHandler) setFs(fs filesystem) { +func (h *mockDigitalPinAccess) setFs(fs filesystem) { // do nothing return } diff --git a/system/system.go b/system/system.go index 967a4f62..b58316b9 100644 --- a/system/system.go +++ b/system/system.go @@ -52,14 +52,14 @@ type Accesser struct { // NewAccesser returns a accesser to native system call, native file system and the chosen digital pin access. // Digital pin accesser can be empty or "sysfs", otherwise it will be automatically chosen. -func NewAccesser(digitalPinAccesser ...string) *Accesser { +func NewAccesser(digitalPinAccess ...string) *Accesser { s := Accesser{ sys: &nativeSyscall{}, fs: &nativeFilesystem{}, } a := "sysfs" - if len(digitalPinAccesser) > 0 && digitalPinAccesser[0] != "" { - a = digitalPinAccesser[0] + if len(digitalPinAccess) > 0 && digitalPinAccess[0] != "" { + a = digitalPinAccess[0] } if a != "sysfs" { dpa := &gpiodDigitalPinAccess{fs: s.fs} @@ -78,13 +78,21 @@ func NewAccesser(digitalPinAccesser ...string) *Accesser { return &s } -// UseMockDigitalPinWithMockFs sets the digital pin handler accesser to the mocked one. Used only for tests. -func (a *Accesser) UseMockDigitalPinWithMockFs(files []string) *mockDigitalPinHandler { +// UseDigitalPinAccessWithMockFs sets the digital pin handler accesser to the chosen one. Used only for tests. +func (a *Accesser) UseDigitalPinAccessWithMockFs(digitalPinAccess string, files []string) digitalPinAccesser { fs := newMockFilesystem(files) - mdph := &mockDigitalPinHandler{fs: fs} + var dph digitalPinAccesser + switch digitalPinAccess { + case "sysfs": + dph = &sysfsDigitalPinAccess{fs: fs} + case "cdev": + dph = &gpiodDigitalPinAccess{fs: fs} + default: + dph = &mockDigitalPinAccess{fs: fs} + } a.fs = fs - a.digitalPinAccess = mdph - return mdph + a.digitalPinAccess = dph + return dph } // UseMockSyscall sets the Syscall implementation of the accesser to the mocked one. Used only for tests.