Bugfix/Improvement: Use DigitalPinsAdaptor for platforms

This commit is contained in:
Thomas Kohler 2022-12-01 17:33:33 +01:00 committed by GitHub
parent 45ee9c3644
commit 89afbcf85b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 623 additions and 1039 deletions

View File

@ -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)

View File

@ -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"))
}

View File

@ -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

View File

@ -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)

View File

@ -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)
}

View File

@ -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"})

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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) {

View File

@ -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")
}

View File

@ -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) {

View File

@ -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",
}

View File

@ -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

View File

@ -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()

View File

@ -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) {

View File

@ -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
}

View File

@ -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)

View File

@ -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) {

View File

@ -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)

View File

@ -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
}

View File

@ -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.