gobot.io/x/gobot/v2@v2.1.0/platforms/jetson/jetson_adaptor.go (about)

     1  package jetson
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sync"
     7  
     8  	multierror "github.com/hashicorp/go-multierror"
     9  	"gobot.io/x/gobot/v2"
    10  	"gobot.io/x/gobot/v2/platforms/adaptors"
    11  	"gobot.io/x/gobot/v2/system"
    12  )
    13  
    14  const (
    15  	pwmPeriodDefault = 3000000 // 3 ms = 333 Hz
    16  
    17  	defaultI2cBusNumber = 1
    18  
    19  	defaultSpiBusNumber  = 0
    20  	defaultSpiChipNumber = 0
    21  	defaultSpiMode       = 0
    22  	defaultSpiBitsNumber = 8
    23  	defaultSpiMaxSpeed   = 10000000
    24  )
    25  
    26  // Adaptor is the Gobot adaptor for the Jetson Nano
    27  type Adaptor struct {
    28  	name    string
    29  	sys     *system.Accesser
    30  	mutex   sync.Mutex
    31  	pwmPins map[string]gobot.PWMPinner
    32  	*adaptors.DigitalPinsAdaptor
    33  	*adaptors.I2cBusAdaptor
    34  	*adaptors.SpiBusAdaptor
    35  }
    36  
    37  // NewAdaptor creates a Jetson Nano adaptor
    38  //
    39  // Optional parameters:
    40  //
    41  //	adaptors.WithGpiodAccess():	use character device gpiod driver instead of sysfs
    42  //	adaptors.WithSpiGpioAccess(sclk, nss, mosi, miso):	use GPIO's instead of /dev/spidev#.#
    43  func NewAdaptor(opts ...func(adaptors.Optioner)) *Adaptor {
    44  	sys := system.NewAccesser()
    45  	c := &Adaptor{
    46  		name: gobot.DefaultName("JetsonNano"),
    47  		sys:  sys,
    48  	}
    49  	c.DigitalPinsAdaptor = adaptors.NewDigitalPinsAdaptor(sys, c.translateDigitalPin, opts...)
    50  	c.I2cBusAdaptor = adaptors.NewI2cBusAdaptor(sys, c.validateI2cBusNumber, defaultI2cBusNumber)
    51  	c.SpiBusAdaptor = adaptors.NewSpiBusAdaptor(sys, c.validateSpiBusNumber, defaultSpiBusNumber, defaultSpiChipNumber,
    52  		defaultSpiMode, defaultSpiBitsNumber, defaultSpiMaxSpeed)
    53  	return c
    54  }
    55  
    56  // Name returns the Adaptor's name
    57  func (c *Adaptor) Name() string {
    58  	c.mutex.Lock()
    59  	defer c.mutex.Unlock()
    60  
    61  	return c.name
    62  }
    63  
    64  // SetName sets the Adaptor's name
    65  func (c *Adaptor) SetName(n string) {
    66  	c.mutex.Lock()
    67  	defer c.mutex.Unlock()
    68  
    69  	c.name = n
    70  }
    71  
    72  // Connect create new connection to board and pins.
    73  func (c *Adaptor) Connect() error {
    74  	c.mutex.Lock()
    75  	defer c.mutex.Unlock()
    76  
    77  	if err := c.SpiBusAdaptor.Connect(); err != nil {
    78  		return err
    79  	}
    80  
    81  	if err := c.I2cBusAdaptor.Connect(); err != nil {
    82  		return err
    83  	}
    84  
    85  	c.pwmPins = make(map[string]gobot.PWMPinner)
    86  	return c.DigitalPinsAdaptor.Connect()
    87  }
    88  
    89  // Finalize closes connection to board and pins
    90  func (c *Adaptor) Finalize() error {
    91  	c.mutex.Lock()
    92  	defer c.mutex.Unlock()
    93  
    94  	err := c.DigitalPinsAdaptor.Finalize()
    95  
    96  	for _, pin := range c.pwmPins {
    97  		if pin != nil {
    98  			if perr := pin.Unexport(); err != nil {
    99  				err = multierror.Append(err, perr)
   100  			}
   101  		}
   102  	}
   103  	c.pwmPins = nil
   104  
   105  	if e := c.I2cBusAdaptor.Finalize(); e != nil {
   106  		err = multierror.Append(err, e)
   107  	}
   108  
   109  	if e := c.SpiBusAdaptor.Finalize(); e != nil {
   110  		err = multierror.Append(err, e)
   111  	}
   112  	return err
   113  }
   114  
   115  // PWMPin returns a Jetson Nano. PWMPin which provides the gobot.PWMPinner interface
   116  func (c *Adaptor) PWMPin(pin string) (gobot.PWMPinner, error) {
   117  	c.mutex.Lock()
   118  	defer c.mutex.Unlock()
   119  
   120  	return c.pwmPin(pin)
   121  }
   122  
   123  // PwmWrite writes a PWM signal to the specified pin
   124  func (c *Adaptor) PwmWrite(pin string, val byte) (err error) {
   125  	c.mutex.Lock()
   126  	defer c.mutex.Unlock()
   127  
   128  	sysPin, err := c.pwmPin(pin)
   129  	if err != nil {
   130  		return err
   131  	}
   132  
   133  	duty := uint32(gobot.FromScale(float64(val), 0, 255) * float64(pwmPeriodDefault))
   134  	return sysPin.SetDutyCycle(duty)
   135  }
   136  
   137  // ServoWrite writes a servo signal to the specified pin
   138  func (c *Adaptor) ServoWrite(pin string, angle byte) (err error) {
   139  	c.mutex.Lock()
   140  	defer c.mutex.Unlock()
   141  
   142  	sysPin, err := c.pwmPin(pin)
   143  	if err != nil {
   144  		return err
   145  	}
   146  
   147  	duty := uint32(gobot.FromScale(float64(angle), 0, 180) * float64(pwmPeriodDefault))
   148  	return sysPin.SetDutyCycle(duty)
   149  }
   150  
   151  func (c *Adaptor) pwmPin(pin string) (gobot.PWMPinner, error) {
   152  	if c.pwmPins == nil {
   153  		return nil, fmt.Errorf("not connected")
   154  	}
   155  
   156  	if c.pwmPins[pin] != nil {
   157  		return c.pwmPins[pin], nil
   158  	}
   159  
   160  	fn, err := c.translatePwmPin(pin)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  
   165  	c.pwmPins[pin] = NewPWMPin(c.sys, "/sys/class/pwm/pwmchip0", fn)
   166  	if err := c.pwmPins[pin].Export(); err != nil {
   167  		return nil, err
   168  	}
   169  	if err := c.pwmPins[pin].SetPeriod(pwmPeriodDefault); err != nil {
   170  		return nil, err
   171  	}
   172  	if err := c.pwmPins[pin].SetEnabled(true); err != nil {
   173  		return nil, err
   174  	}
   175  
   176  	return c.pwmPins[pin], nil
   177  }
   178  
   179  func (c *Adaptor) validateSpiBusNumber(busNr int) error {
   180  	// Valid bus numbers are [0,1] which corresponds to /dev/spidev0.x through /dev/spidev1.x.
   181  	// x is the chip number <255
   182  	if (busNr < 0) || (busNr > 1) {
   183  		return fmt.Errorf("Bus number %d out of range", busNr)
   184  	}
   185  	return nil
   186  }
   187  
   188  func (c *Adaptor) validateI2cBusNumber(busNr int) error {
   189  	// Valid bus number is [0..1] which corresponds to /dev/i2c-0 through /dev/i2c-1.
   190  	if (busNr < 0) || (busNr > 1) {
   191  		return fmt.Errorf("Bus number %d out of range", busNr)
   192  	}
   193  	return nil
   194  }
   195  
   196  func (c *Adaptor) translateDigitalPin(id string) (string, int, error) {
   197  	if line, ok := gpioPins[id]; ok {
   198  		return "", line, nil
   199  	}
   200  	return "", -1, fmt.Errorf("'%s' is not a valid id for a digital pin", id)
   201  }
   202  
   203  func (c *Adaptor) translatePwmPin(pin string) (fn string, err error) {
   204  	if fn, ok := pwmPins[pin]; ok {
   205  		return fn, nil
   206  	}
   207  	return "", errors.New("Not a valid pin")
   208  }