gobot.io/x/gobot@v1.16.0/platforms/beaglebone/beaglebone_adaptor.go (about)

     1  package beaglebone
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"strconv"
     9  	"strings"
    10  	"sync"
    11  
    12  	multierror "github.com/hashicorp/go-multierror"
    13  	"gobot.io/x/gobot"
    14  	"gobot.io/x/gobot/drivers/i2c"
    15  	"gobot.io/x/gobot/drivers/spi"
    16  	"gobot.io/x/gobot/sysfs"
    17  )
    18  
    19  type pwmPinData struct {
    20  	channel int
    21  	path    string
    22  }
    23  
    24  const pwmDefaultPeriod = 500000
    25  
    26  // Adaptor is the gobot.Adaptor representation for the Beaglebone Black/Green
    27  type Adaptor struct {
    28  	name               string
    29  	digitalPins        []*sysfs.DigitalPin
    30  	pwmPins            map[string]*sysfs.PWMPin
    31  	i2cBuses           map[int]i2c.I2cDevice
    32  	usrLed             string
    33  	analogPath         string
    34  	pinMap             map[string]int
    35  	pwmPinMap          map[string]pwmPinData
    36  	analogPinMap       map[string]string
    37  	mutex              *sync.Mutex
    38  	findPin            func(pinPath string) (string, error)
    39  	spiDefaultBus      int
    40  	spiDefaultChip     int
    41  	spiBuses           [2]spi.Connection
    42  	spiDefaultMode     int
    43  	spiDefaultMaxSpeed int64
    44  }
    45  
    46  // NewAdaptor returns a new Beaglebone Black/Green Adaptor
    47  func NewAdaptor() *Adaptor {
    48  	b := &Adaptor{
    49  		name:         gobot.DefaultName("BeagleboneBlack"),
    50  		digitalPins:  make([]*sysfs.DigitalPin, 120),
    51  		pwmPins:      make(map[string]*sysfs.PWMPin),
    52  		i2cBuses:     make(map[int]i2c.I2cDevice),
    53  		mutex:        &sync.Mutex{},
    54  		pinMap:       bbbPinMap,
    55  		pwmPinMap:    bbbPwmPinMap,
    56  		analogPinMap: bbbAnalogPinMap,
    57  		findPin: func(pinPath string) (string, error) {
    58  			files, err := filepath.Glob(pinPath)
    59  			return files[0], err
    60  		},
    61  	}
    62  
    63  	b.setPaths()
    64  	return b
    65  }
    66  
    67  func (b *Adaptor) setPaths() {
    68  	b.usrLed = "/sys/class/leds/beaglebone:green:"
    69  	b.analogPath = "/sys/bus/iio/devices/iio:device0"
    70  
    71  	b.spiDefaultBus = 0
    72  	b.spiDefaultMode = 0
    73  	b.spiDefaultMaxSpeed = 500000
    74  }
    75  
    76  // Name returns the Adaptor name
    77  func (b *Adaptor) Name() string { return b.name }
    78  
    79  // SetName sets the Adaptor name
    80  func (b *Adaptor) SetName(n string) { b.name = n }
    81  
    82  // Connect initializes the pwm and analog dts.
    83  func (b *Adaptor) Connect() error {
    84  	return nil
    85  }
    86  
    87  // Finalize releases all i2c devices and exported analog, digital, pwm pins.
    88  func (b *Adaptor) Finalize() (err error) {
    89  	b.mutex.Lock()
    90  	defer b.mutex.Unlock()
    91  
    92  	for _, pin := range b.digitalPins {
    93  		if pin != nil {
    94  			if e := pin.Unexport(); e != nil {
    95  				err = multierror.Append(err, e)
    96  			}
    97  		}
    98  	}
    99  	for _, pin := range b.pwmPins {
   100  		if pin != nil {
   101  			if e := pin.Unexport(); e != nil {
   102  				err = multierror.Append(err, e)
   103  			}
   104  		}
   105  	}
   106  	for _, bus := range b.i2cBuses {
   107  		if bus != nil {
   108  			if e := bus.Close(); e != nil {
   109  				err = multierror.Append(err, e)
   110  			}
   111  		}
   112  	}
   113  	for _, bus := range b.spiBuses {
   114  		if bus != nil {
   115  			if e := bus.Close(); e != nil {
   116  				err = multierror.Append(err, e)
   117  			}
   118  		}
   119  	}
   120  	return
   121  }
   122  
   123  // PwmWrite writes the 0-254 value to the specified pin
   124  func (b *Adaptor) PwmWrite(pin string, val byte) (err error) {
   125  	pwmPin, err := b.PWMPin(pin)
   126  	if err != nil {
   127  		return
   128  	}
   129  	period, err := pwmPin.Period()
   130  	if err != nil {
   131  		return err
   132  	}
   133  	duty := gobot.FromScale(float64(val), 0, 255.0)
   134  	return pwmPin.SetDutyCycle(uint32(float64(period) * duty))
   135  }
   136  
   137  // ServoWrite writes a servo signal to the specified pin
   138  func (b *Adaptor) ServoWrite(pin string, angle byte) (err error) {
   139  	pwmPin, err := b.PWMPin(pin)
   140  	if err != nil {
   141  		return
   142  	}
   143  
   144  	// TODO: take into account the actual period setting, not just assume default
   145  	const minDuty = 100 * 0.0005 * pwmDefaultPeriod
   146  	const maxDuty = 100 * 0.0020 * pwmDefaultPeriod
   147  	duty := uint32(gobot.ToScale(gobot.FromScale(float64(angle), 0, 180), minDuty, maxDuty))
   148  	return pwmPin.SetDutyCycle(duty)
   149  }
   150  
   151  // DigitalRead returns a digital value from specified pin
   152  func (b *Adaptor) DigitalRead(pin string) (val int, err error) {
   153  	sysfsPin, err := b.DigitalPin(pin, sysfs.IN)
   154  	if err != nil {
   155  		return
   156  	}
   157  	return sysfsPin.Read()
   158  }
   159  
   160  // DigitalWrite writes a digital value to specified pin.
   161  // valid usr pin values are usr0, usr1, usr2 and usr3
   162  func (b *Adaptor) DigitalWrite(pin string, val byte) (err error) {
   163  	if strings.Contains(pin, "usr") {
   164  		fi, e := sysfs.OpenFile(b.usrLed+pin+"/brightness", os.O_WRONLY|os.O_APPEND, 0666)
   165  		defer fi.Close()
   166  		if e != nil {
   167  			return e
   168  		}
   169  		_, err = fi.WriteString(strconv.Itoa(int(val)))
   170  		return err
   171  	}
   172  	sysfsPin, err := b.DigitalPin(pin, sysfs.OUT)
   173  	if err != nil {
   174  		return err
   175  	}
   176  	return sysfsPin.Write(int(val))
   177  }
   178  
   179  // DigitalPin retrieves digital pin value by name
   180  func (b *Adaptor) DigitalPin(pin string, dir string) (sysfsPin sysfs.DigitalPinner, err error) {
   181  	b.mutex.Lock()
   182  	defer b.mutex.Unlock()
   183  
   184  	i, err := b.translatePin(pin)
   185  	if err != nil {
   186  		return
   187  	}
   188  	if b.digitalPins[i] == nil {
   189  		b.digitalPins[i] = sysfs.NewDigitalPin(i)
   190  		if err = muxPin(pin, "gpio"); err != nil {
   191  			return
   192  		}
   193  
   194  		err := b.digitalPins[i].Export()
   195  		if err != nil {
   196  			return nil, err
   197  		}
   198  	}
   199  	if err = b.digitalPins[i].Direction(dir); err != nil {
   200  		return
   201  	}
   202  	return b.digitalPins[i], nil
   203  }
   204  
   205  // PWMPin returns matched pwmPin for specified pin number
   206  func (b *Adaptor) PWMPin(pin string) (sysfsPin sysfs.PWMPinner, err error) {
   207  	b.mutex.Lock()
   208  	defer b.mutex.Unlock()
   209  
   210  	pinInfo, err := b.translatePwmPin(pin)
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  
   215  	if b.pwmPins[pin] == nil {
   216  		newPin := sysfs.NewPWMPin(pinInfo.channel)
   217  		if err = muxPin(pin, "pwm"); err != nil {
   218  			return
   219  		}
   220  
   221  		if newPin.Path, err = b.findPin(pinInfo.path); err != nil {
   222  			return
   223  		}
   224  		if err = newPin.Export(); err != nil {
   225  			return
   226  		}
   227  		if err = newPin.SetPeriod(pwmDefaultPeriod); err != nil {
   228  			return
   229  		}
   230  		if err = newPin.Enable(true); err != nil {
   231  			return
   232  		}
   233  		b.pwmPins[pin] = newPin
   234  	}
   235  
   236  	sysfsPin = b.pwmPins[pin]
   237  
   238  	return
   239  }
   240  
   241  // AnalogRead returns an analog value from specified pin
   242  func (b *Adaptor) AnalogRead(pin string) (val int, err error) {
   243  	analogPin, err := b.translateAnalogPin(pin)
   244  	if err != nil {
   245  		return
   246  	}
   247  	fi, err := sysfs.OpenFile(fmt.Sprintf("%v/%v", b.analogPath, analogPin), os.O_RDONLY, 0644)
   248  	defer fi.Close()
   249  
   250  	if err != nil {
   251  		return
   252  	}
   253  
   254  	var buf = make([]byte, 1024)
   255  	_, err = fi.Read(buf)
   256  	if err != nil {
   257  		return
   258  	}
   259  
   260  	val, _ = strconv.Atoi(strings.Split(string(buf), "\n")[0])
   261  	return
   262  }
   263  
   264  // GetConnection returns a connection to a device on a specified bus.
   265  // Valid bus number is either 0 or 2 which corresponds to /dev/i2c-0 or /dev/i2c-2.
   266  func (b *Adaptor) GetConnection(address int, bus int) (connection i2c.Connection, err error) {
   267  	b.mutex.Lock()
   268  	defer b.mutex.Unlock()
   269  
   270  	if (bus != 0) && (bus != 2) {
   271  		return nil, fmt.Errorf("Bus number %d out of range", bus)
   272  	}
   273  	if b.i2cBuses[bus] == nil {
   274  		b.i2cBuses[bus], err = sysfs.NewI2cDevice(fmt.Sprintf("/dev/i2c-%d", bus))
   275  	}
   276  	return i2c.NewConnection(b.i2cBuses[bus], address), err
   277  }
   278  
   279  // GetDefaultBus returns the default i2c bus for this platform
   280  func (b *Adaptor) GetDefaultBus() int {
   281  	return 2
   282  }
   283  
   284  // GetSpiConnection returns an spi connection to a device on a specified bus.
   285  // Valid bus number is [0..1] which corresponds to /dev/spidev0.0 through /dev/spidev0.1.
   286  func (b *Adaptor) GetSpiConnection(busNum, chipNum, mode, bits int, maxSpeed int64) (connection spi.Connection, err error) {
   287  	b.mutex.Lock()
   288  	defer b.mutex.Unlock()
   289  
   290  	if (busNum < 0) || (busNum > 1) {
   291  		return nil, fmt.Errorf("Bus number %d out of range", busNum)
   292  	}
   293  
   294  	if b.spiBuses[busNum] == nil {
   295  		b.spiBuses[busNum], err = spi.GetSpiConnection(busNum, chipNum, mode, bits, maxSpeed)
   296  	}
   297  
   298  	return b.spiBuses[busNum], err
   299  }
   300  
   301  // GetSpiDefaultBus returns the default spi bus for this platform.
   302  func (b *Adaptor) GetSpiDefaultBus() int {
   303  	return b.spiDefaultBus
   304  }
   305  
   306  // GetSpiDefaultChip returns the default spi chip for this platform.
   307  func (b *Adaptor) GetSpiDefaultChip() int {
   308  	return b.spiDefaultChip
   309  }
   310  
   311  // GetSpiDefaultMode returns the default spi mode for this platform.
   312  func (b *Adaptor) GetSpiDefaultMode() int {
   313  	return b.spiDefaultMode
   314  }
   315  
   316  // GetSpiDefaultBits returns the default spi number of bits for this platform.
   317  func (b *Adaptor) GetSpiDefaultBits() int {
   318  	return 8
   319  }
   320  
   321  // GetSpiDefaultMaxSpeed returns the default spi bus for this platform.
   322  func (b *Adaptor) GetSpiDefaultMaxSpeed() int64 {
   323  	return b.spiDefaultMaxSpeed
   324  }
   325  
   326  // translatePin converts digital pin name to pin position
   327  func (b *Adaptor) translatePin(pin string) (value int, err error) {
   328  	if val, ok := b.pinMap[pin]; ok {
   329  		value = val
   330  	} else {
   331  		err = errors.New("Not a valid pin")
   332  	}
   333  	return
   334  }
   335  
   336  func (b *Adaptor) translatePwmPin(pin string) (p pwmPinData, err error) {
   337  	if val, ok := b.pwmPinMap[pin]; ok {
   338  		p = val
   339  	} else {
   340  		err = errors.New("Not a valid PWM pin")
   341  	}
   342  	return
   343  }
   344  
   345  // translateAnalogPin converts analog pin name to pin position
   346  func (b *Adaptor) translateAnalogPin(pin string) (value string, err error) {
   347  	if val, ok := b.analogPinMap[pin]; ok {
   348  		value = val
   349  	} else {
   350  		err = errors.New("Not a valid analog pin")
   351  	}
   352  	return
   353  }
   354  
   355  func muxPin(pin, cmd string) error {
   356  	path := fmt.Sprintf("/sys/devices/platform/ocp/ocp:%s_pinmux/state", pin)
   357  	fi, e := sysfs.OpenFile(path, os.O_WRONLY, 0666)
   358  	defer fi.Close()
   359  	if e != nil {
   360  		return e
   361  	}
   362  	_, e = fi.WriteString(cmd)
   363  	return e
   364  }