gobot.io/x/gobot@v1.16.0/platforms/upboard/up2/adaptor.go (about)

     1  package up2
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"strconv"
     8  	"sync"
     9  
    10  	multierror "github.com/hashicorp/go-multierror"
    11  	"gobot.io/x/gobot"
    12  	"gobot.io/x/gobot/drivers/i2c"
    13  	"gobot.io/x/gobot/drivers/spi"
    14  	"gobot.io/x/gobot/sysfs"
    15  )
    16  
    17  const (
    18  	// LEDRed is the built-in red LED.
    19  	LEDRed = "red"
    20  
    21  	// LEDBlue is the built-in blue LED.
    22  	LEDBlue = "blue"
    23  
    24  	// LEDGreen is the built-in green LED.
    25  	LEDGreen = "green"
    26  
    27  	// LEDYellow is the built-in yellow LED.
    28  	LEDYellow = "yellow"
    29  )
    30  
    31  type sysfsPin struct {
    32  	pin    int
    33  	pwmPin int
    34  }
    35  
    36  // Adaptor represents a Gobot Adaptor for the Upboard UP2
    37  type Adaptor struct {
    38  	name               string
    39  	pinmap             map[string]sysfsPin
    40  	ledPath            string
    41  	digitalPins        map[int]*sysfs.DigitalPin
    42  	pwmPins            map[int]*sysfs.PWMPin
    43  	i2cBuses           [6]i2c.I2cDevice
    44  	mutex              *sync.Mutex
    45  	spiDefaultBus      int
    46  	spiDefaultChip     int
    47  	spiBuses           [2]spi.Connection
    48  	spiDefaultMode     int
    49  	spiDefaultMaxSpeed int64
    50  }
    51  
    52  // NewAdaptor creates a UP2 Adaptor
    53  func NewAdaptor() *Adaptor {
    54  	c := &Adaptor{
    55  		name:    gobot.DefaultName("UP2"),
    56  		mutex:   &sync.Mutex{},
    57  		ledPath: "/sys/class/leds/upboard:%s:/brightness",
    58  	}
    59  
    60  	c.setPins()
    61  	return c
    62  }
    63  
    64  // Name returns the name of the Adaptor
    65  func (c *Adaptor) Name() string { return c.name }
    66  
    67  // SetName sets the name of the Adaptor
    68  func (c *Adaptor) SetName(n string) { c.name = n }
    69  
    70  // Connect initializes the board
    71  func (c *Adaptor) Connect() (err error) {
    72  	return nil
    73  }
    74  
    75  // Finalize closes connection to board and pins
    76  func (c *Adaptor) Finalize() (err error) {
    77  	c.mutex.Lock()
    78  	defer c.mutex.Unlock()
    79  
    80  	for _, pin := range c.digitalPins {
    81  		if pin != nil {
    82  			if e := pin.Unexport(); e != nil {
    83  				err = multierror.Append(err, e)
    84  			}
    85  		}
    86  	}
    87  	for _, pin := range c.pwmPins {
    88  		if pin != nil {
    89  			if errs := pin.Enable(false); errs != nil {
    90  				err = multierror.Append(err, errs)
    91  			}
    92  			if errs := pin.Unexport(); errs != nil {
    93  				err = multierror.Append(err, errs)
    94  			}
    95  		}
    96  	}
    97  	for _, bus := range c.i2cBuses {
    98  		if bus != nil {
    99  			if e := bus.Close(); e != nil {
   100  				err = multierror.Append(err, e)
   101  			}
   102  		}
   103  	}
   104  	for _, bus := range c.spiBuses {
   105  		if bus != nil {
   106  			if e := bus.Close(); e != nil {
   107  				err = multierror.Append(err, e)
   108  			}
   109  		}
   110  	}
   111  
   112  	return
   113  }
   114  
   115  // DigitalRead reads digital value from the specified pin.
   116  func (c *Adaptor) DigitalRead(pin string) (val int, err error) {
   117  	sysfsPin, err := c.DigitalPin(pin, sysfs.IN)
   118  	if err != nil {
   119  		return
   120  	}
   121  	return sysfsPin.Read()
   122  }
   123  
   124  // DigitalWrite writes digital value to the specified pin.
   125  func (c *Adaptor) DigitalWrite(pin string, val byte) (err error) {
   126  	// is it one of the built-in LEDs?
   127  	if pin == LEDRed || pin == LEDBlue || pin == LEDGreen || pin == LEDYellow {
   128  		pinPath := fmt.Sprintf(c.ledPath, pin)
   129  		fi, e := sysfs.OpenFile(pinPath, os.O_WRONLY|os.O_APPEND, 0666)
   130  		defer fi.Close()
   131  		if e != nil {
   132  			return e
   133  		}
   134  		_, err = fi.WriteString(strconv.Itoa(int(val)))
   135  		return err
   136  	}
   137  	// one of the normal GPIO pins, then
   138  	sysfsPin, err := c.DigitalPin(pin, sysfs.OUT)
   139  	if err != nil {
   140  		return err
   141  	}
   142  	return sysfsPin.Write(int(val))
   143  }
   144  
   145  // PwmWrite writes a PWM signal to the specified pin
   146  func (c *Adaptor) PwmWrite(pin string, val byte) (err error) {
   147  	pwmPin, err := c.PWMPin(pin)
   148  	if err != nil {
   149  		return
   150  	}
   151  	period, err := pwmPin.Period()
   152  	if err != nil {
   153  		return err
   154  	}
   155  	duty := gobot.FromScale(float64(val), 0, 255.0)
   156  	return pwmPin.SetDutyCycle(uint32(float64(period) * duty))
   157  }
   158  
   159  // TODO: take into account the actual period setting, not just assume default
   160  const pwmPeriod = 10000000
   161  
   162  // ServoWrite writes a servo signal to the specified pin
   163  func (c *Adaptor) ServoWrite(pin string, angle byte) (err error) {
   164  	pwmPin, err := c.PWMPin(pin)
   165  	if err != nil {
   166  		return
   167  	}
   168  
   169  	// 0.5 ms => -90
   170  	// 1.5 ms =>   0
   171  	// 2.0 ms =>  90
   172  	const minDuty = 100 * 0.0005 * pwmPeriod
   173  	const maxDuty = 100 * 0.0020 * pwmPeriod
   174  	duty := uint32(gobot.ToScale(gobot.FromScale(float64(angle), 0, 180), minDuty, maxDuty))
   175  	return pwmPin.SetDutyCycle(duty)
   176  }
   177  
   178  // DigitalPin returns matched digitalPin for specified values
   179  func (c *Adaptor) DigitalPin(pin string, dir string) (sysfsPin sysfs.DigitalPinner, err error) {
   180  	c.mutex.Lock()
   181  	defer c.mutex.Unlock()
   182  
   183  	i, err := c.translatePin(pin)
   184  
   185  	if err != nil {
   186  		return
   187  	}
   188  
   189  	if c.digitalPins[i] == nil {
   190  		c.digitalPins[i] = sysfs.NewDigitalPin(i)
   191  		if err = c.digitalPins[i].Export(); err != nil {
   192  			return
   193  		}
   194  	}
   195  
   196  	if err = c.digitalPins[i].Direction(dir); err != nil {
   197  		return
   198  	}
   199  
   200  	return c.digitalPins[i], nil
   201  }
   202  
   203  // PWMPin returns matched pwmPin for specified pin number
   204  func (c *Adaptor) PWMPin(pin string) (sysfsPin sysfs.PWMPinner, err error) {
   205  	c.mutex.Lock()
   206  	defer c.mutex.Unlock()
   207  
   208  	i, err := c.translatePwmPin(pin)
   209  	if err != nil {
   210  		return nil, err
   211  	}
   212  	if i == -1 {
   213  		return nil, errors.New("Not a PWM pin")
   214  	}
   215  
   216  	if c.pwmPins[i] == nil {
   217  		newPin := sysfs.NewPWMPin(i)
   218  		if err = newPin.Export(); err != nil {
   219  			return
   220  		}
   221  		// Make sure pwm is disabled when setting polarity
   222  		if err = newPin.Enable(false); err != nil {
   223  			return
   224  		}
   225  		if err = newPin.InvertPolarity(false); err != nil {
   226  			return
   227  		}
   228  		if err = newPin.Enable(true); err != nil {
   229  			return
   230  		}
   231  		if err = newPin.SetPeriod(10000000); err != nil {
   232  			return
   233  		}
   234  		c.pwmPins[i] = newPin
   235  	}
   236  
   237  	sysfsPin = c.pwmPins[i]
   238  	return
   239  }
   240  
   241  // GetConnection returns a connection to a device on a specified bus.
   242  // Valid bus number is [5..6] which corresponds to /dev/i2c-5 through /dev/i2c-6.
   243  func (c *Adaptor) GetConnection(address int, bus int) (connection i2c.Connection, err error) {
   244  	c.mutex.Lock()
   245  	defer c.mutex.Unlock()
   246  
   247  	if (bus < 5) || (bus > 6) {
   248  		return nil, fmt.Errorf("Bus number %d out of range", bus)
   249  	}
   250  	if c.i2cBuses[bus] == nil {
   251  		c.i2cBuses[bus], err = sysfs.NewI2cDevice(fmt.Sprintf("/dev/i2c-%d", bus))
   252  	}
   253  	return i2c.NewConnection(c.i2cBuses[bus], address), err
   254  }
   255  
   256  // GetDefaultBus returns the default i2c bus for this platform
   257  func (c *Adaptor) GetDefaultBus() int {
   258  	return 5
   259  }
   260  
   261  // GetSpiConnection returns an spi connection to a device on a specified bus.
   262  // Valid bus number is [0..1] which corresponds to /dev/spidev0.0 through /dev/spidev0.1.
   263  func (c *Adaptor) GetSpiConnection(busNum, chipNum, mode, bits int, maxSpeed int64) (connection spi.Connection, err error) {
   264  	c.mutex.Lock()
   265  	defer c.mutex.Unlock()
   266  
   267  	if (busNum < 0) || (busNum > 1) {
   268  		return nil, fmt.Errorf("Bus number %d out of range", busNum)
   269  	}
   270  
   271  	if c.spiBuses[busNum] == nil {
   272  		c.spiBuses[busNum], err = spi.GetSpiConnection(busNum, chipNum, mode, bits, maxSpeed)
   273  	}
   274  
   275  	return c.spiBuses[busNum], err
   276  }
   277  
   278  // GetSpiDefaultBus returns the default spi bus for this platform.
   279  func (c *Adaptor) GetSpiDefaultBus() int {
   280  	return c.spiDefaultBus
   281  }
   282  
   283  // GetSpiDefaultChip returns the default spi chip for this platform.
   284  func (c *Adaptor) GetSpiDefaultChip() int {
   285  	return c.spiDefaultChip
   286  }
   287  
   288  // GetSpiDefaultMode returns the default spi mode for this platform.
   289  func (c *Adaptor) GetSpiDefaultMode() int {
   290  	return c.spiDefaultMode
   291  }
   292  
   293  // GetSpiDefaultBits returns the default spi number of bits for this platform.
   294  func (c *Adaptor) GetSpiDefaultBits() int {
   295  	return 8
   296  }
   297  
   298  // GetSpiDefaultMaxSpeed returns the default spi max speed for this platform.
   299  func (c *Adaptor) GetSpiDefaultMaxSpeed() int64 {
   300  	return c.spiDefaultMaxSpeed
   301  }
   302  
   303  func (c *Adaptor) setPins() {
   304  	c.digitalPins = make(map[int]*sysfs.DigitalPin)
   305  	c.pwmPins = make(map[int]*sysfs.PWMPin)
   306  	c.pinmap = fixedPins
   307  
   308  	c.spiDefaultBus = 0
   309  	c.spiDefaultMode = 0
   310  	c.spiDefaultMaxSpeed = 500000
   311  }
   312  
   313  func (c *Adaptor) translatePin(pin string) (i int, err error) {
   314  	if val, ok := c.pinmap[pin]; ok {
   315  		i = val.pin
   316  	} else {
   317  		err = errors.New("Not a valid pin")
   318  	}
   319  	return
   320  }
   321  
   322  func (c *Adaptor) translatePwmPin(pin string) (i int, err error) {
   323  	if val, ok := c.pinmap[pin]; ok {
   324  		i = val.pwmPin
   325  	} else {
   326  		err = errors.New("Not a valid pin")
   327  	}
   328  	return
   329  }