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

     1  package tinkerboard
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sync"
     7  
     8  	multierror "github.com/hashicorp/go-multierror"
     9  	"gobot.io/x/gobot"
    10  	"gobot.io/x/gobot/drivers/i2c"
    11  	"gobot.io/x/gobot/sysfs"
    12  )
    13  
    14  type sysfsPin struct {
    15  	pin    int
    16  	pwmPin int
    17  }
    18  
    19  // Adaptor represents a Gobot Adaptor for the ASUS Tinker Board
    20  type Adaptor struct {
    21  	name        string
    22  	pinmap      map[string]sysfsPin
    23  	digitalPins map[int]*sysfs.DigitalPin
    24  	pwmPins     map[int]*sysfs.PWMPin
    25  	i2cBuses    [5]i2c.I2cDevice
    26  	mutex       *sync.Mutex
    27  }
    28  
    29  // NewAdaptor creates a Tinkerboard Adaptor
    30  func NewAdaptor() *Adaptor {
    31  	c := &Adaptor{
    32  		name:  gobot.DefaultName("Tinker Board"),
    33  		mutex: &sync.Mutex{},
    34  	}
    35  
    36  	c.setPins()
    37  	return c
    38  }
    39  
    40  // Name returns the name of the Adaptor
    41  func (c *Adaptor) Name() string { return c.name }
    42  
    43  // SetName sets the name of the Adaptor
    44  func (c *Adaptor) SetName(n string) { c.name = n }
    45  
    46  // Connect initializes the board
    47  func (c *Adaptor) Connect() (err error) {
    48  	return nil
    49  }
    50  
    51  // Finalize closes connection to board and pins
    52  func (c *Adaptor) Finalize() (err error) {
    53  	c.mutex.Lock()
    54  	defer c.mutex.Unlock()
    55  
    56  	for _, pin := range c.digitalPins {
    57  		if pin != nil {
    58  			if e := pin.Unexport(); e != nil {
    59  				err = multierror.Append(err, e)
    60  			}
    61  		}
    62  	}
    63  	for _, pin := range c.pwmPins {
    64  		if pin != nil {
    65  			if errs := pin.Enable(false); errs != nil {
    66  				err = multierror.Append(err, errs)
    67  			}
    68  			if errs := pin.Unexport(); errs != nil {
    69  				err = multierror.Append(err, errs)
    70  			}
    71  		}
    72  	}
    73  	for _, bus := range c.i2cBuses {
    74  		if bus != nil {
    75  			if e := bus.Close(); e != nil {
    76  				err = multierror.Append(err, e)
    77  			}
    78  		}
    79  	}
    80  	return
    81  }
    82  
    83  // DigitalRead reads digital value from the specified pin.
    84  func (c *Adaptor) DigitalRead(pin string) (val int, err error) {
    85  	sysfsPin, err := c.DigitalPin(pin, sysfs.IN)
    86  	if err != nil {
    87  		return
    88  	}
    89  	return sysfsPin.Read()
    90  }
    91  
    92  // DigitalWrite writes digital value to the specified pin.
    93  func (c *Adaptor) DigitalWrite(pin string, val byte) (err error) {
    94  	sysfsPin, err := c.DigitalPin(pin, sysfs.OUT)
    95  	if err != nil {
    96  		return err
    97  	}
    98  	return sysfsPin.Write(int(val))
    99  }
   100  
   101  // PwmWrite writes a PWM signal to the specified pin
   102  func (c *Adaptor) PwmWrite(pin string, val byte) (err error) {
   103  	pwmPin, err := c.PWMPin(pin)
   104  	if err != nil {
   105  		return
   106  	}
   107  	period, err := pwmPin.Period()
   108  	if err != nil {
   109  		return err
   110  	}
   111  	duty := gobot.FromScale(float64(val), 0, 255.0)
   112  	return pwmPin.SetDutyCycle(uint32(float64(period) * duty))
   113  }
   114  
   115  // TODO: take into account the actual period setting, not just assume default
   116  const pwmPeriod = 10000000
   117  
   118  // ServoWrite writes a servo signal to the specified pin
   119  func (c *Adaptor) ServoWrite(pin string, angle byte) (err error) {
   120  	pwmPin, err := c.PWMPin(pin)
   121  	if err != nil {
   122  		return
   123  	}
   124  
   125  	// 0.5 ms => -90
   126  	// 1.5 ms =>   0
   127  	// 2.0 ms =>  90
   128  	const minDuty = 100 * 0.0005 * pwmPeriod
   129  	const maxDuty = 100 * 0.0020 * pwmPeriod
   130  	duty := uint32(gobot.ToScale(gobot.FromScale(float64(angle), 0, 180), minDuty, maxDuty))
   131  	return pwmPin.SetDutyCycle(duty)
   132  }
   133  
   134  // DigitalPin returns matched digitalPin for specified values
   135  func (c *Adaptor) DigitalPin(pin string, dir string) (sysfsPin sysfs.DigitalPinner, err error) {
   136  	c.mutex.Lock()
   137  	defer c.mutex.Unlock()
   138  
   139  	i, err := c.translatePin(pin)
   140  
   141  	if err != nil {
   142  		return
   143  	}
   144  
   145  	if c.digitalPins[i] == nil {
   146  		c.digitalPins[i] = sysfs.NewDigitalPin(i)
   147  		if err = c.digitalPins[i].Export(); err != nil {
   148  			return
   149  		}
   150  	}
   151  
   152  	if err = c.digitalPins[i].Direction(dir); err != nil {
   153  		return
   154  	}
   155  
   156  	return c.digitalPins[i], nil
   157  }
   158  
   159  // PWMPin returns matched pwmPin for specified pin number
   160  func (c *Adaptor) PWMPin(pin string) (sysfsPin sysfs.PWMPinner, err error) {
   161  	c.mutex.Lock()
   162  	defer c.mutex.Unlock()
   163  
   164  	i, err := c.translatePwmPin(pin)
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  	if i == -1 {
   169  		return nil, errors.New("Not a PWM pin")
   170  	}
   171  
   172  	if c.pwmPins[i] == nil {
   173  		newPin := sysfs.NewPWMPin(i)
   174  		if err = newPin.Export(); err != nil {
   175  			return
   176  		}
   177  		// Make sure pwm is disabled when setting polarity
   178  		if err = newPin.Enable(false); err != nil {
   179  			return
   180  		}
   181  		if err = newPin.InvertPolarity(false); err != nil {
   182  			return
   183  		}
   184  		if err = newPin.Enable(true); err != nil {
   185  			return
   186  		}
   187  		if err = newPin.SetPeriod(10000000); err != nil {
   188  			return
   189  		}
   190  		c.pwmPins[i] = newPin
   191  	}
   192  
   193  	sysfsPin = c.pwmPins[i]
   194  	return
   195  }
   196  
   197  // GetConnection returns a connection to a device on a specified bus.
   198  // Valid bus number is [0..4] which corresponds to /dev/i2c-0 through /dev/i2c-4.
   199  // We don't support "/dev/i2c-6 DesignWare HDMI".
   200  func (c *Adaptor) GetConnection(address int, bus int) (connection i2c.Connection, err error) {
   201  	c.mutex.Lock()
   202  	defer c.mutex.Unlock()
   203  
   204  	if (bus < 0) || (bus > 4) {
   205  		return nil, fmt.Errorf("Bus number %d out of range", bus)
   206  	}
   207  	if c.i2cBuses[bus] == nil {
   208  		c.i2cBuses[bus], err = sysfs.NewI2cDevice(fmt.Sprintf("/dev/i2c-%d", bus))
   209  	}
   210  	return i2c.NewConnection(c.i2cBuses[bus], address), err
   211  }
   212  
   213  // GetDefaultBus returns the default i2c bus for this platform
   214  func (c *Adaptor) GetDefaultBus() int {
   215  	return 1
   216  }
   217  
   218  func (c *Adaptor) setPins() {
   219  	c.digitalPins = make(map[int]*sysfs.DigitalPin)
   220  	c.pwmPins = make(map[int]*sysfs.PWMPin)
   221  	c.pinmap = fixedPins
   222  }
   223  
   224  func (c *Adaptor) translatePin(pin string) (i int, err error) {
   225  	if val, ok := c.pinmap[pin]; ok {
   226  		i = val.pin
   227  	} else {
   228  		err = errors.New("Not a valid pin")
   229  	}
   230  	return
   231  }
   232  
   233  func (c *Adaptor) translatePwmPin(pin string) (i int, err error) {
   234  	if val, ok := c.pinmap[pin]; ok {
   235  		i = val.pwmPin
   236  	} else {
   237  		err = errors.New("Not a valid pin")
   238  	}
   239  	return
   240  }