gobot.io/x/gobot/v2@v2.1.0/platforms/upboard/up2/adaptor.go (about)

     1  package up2
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strconv"
     7  	"sync"
     8  
     9  	multierror "github.com/hashicorp/go-multierror"
    10  	"gobot.io/x/gobot/v2"
    11  	"gobot.io/x/gobot/v2/platforms/adaptors"
    12  	"gobot.io/x/gobot/v2/system"
    13  )
    14  
    15  const (
    16  	// LEDRed is the built-in red LED.
    17  	LEDRed = "red"
    18  	// LEDBlue is the built-in blue LED.
    19  	LEDBlue = "blue"
    20  	// LEDGreen is the built-in green LED.
    21  	LEDGreen = "green"
    22  	// LEDYellow is the built-in yellow LED.
    23  	LEDYellow = "yellow"
    24  
    25  	defaultI2cBusNumber = 5
    26  
    27  	defaultSpiBusNumber  = 0
    28  	defaultSpiChipNumber = 0
    29  	defaultSpiMode       = 0
    30  	defaultSpiBitsNumber = 8
    31  	defaultSpiMaxSpeed   = 500000
    32  )
    33  
    34  type sysfsPin struct {
    35  	pin    int
    36  	pwmPin int
    37  }
    38  
    39  // Adaptor represents a Gobot Adaptor for the Upboard UP2
    40  type Adaptor struct {
    41  	name    string
    42  	sys     *system.Accesser
    43  	mutex   sync.Mutex
    44  	pinmap  map[string]sysfsPin
    45  	ledPath string
    46  	*adaptors.DigitalPinsAdaptor
    47  	*adaptors.PWMPinsAdaptor
    48  	*adaptors.I2cBusAdaptor
    49  	*adaptors.SpiBusAdaptor
    50  }
    51  
    52  // NewAdaptor creates a UP2 Adaptor
    53  //
    54  // Optional parameters:
    55  //
    56  //	adaptors.WithGpiodAccess():	use character device gpiod driver instead of sysfs
    57  //	adaptors.WithSpiGpioAccess(sclk, nss, mosi, miso):	use GPIO's instead of /dev/spidev#.#
    58  func NewAdaptor(opts ...func(adaptors.Optioner)) *Adaptor {
    59  	sys := system.NewAccesser()
    60  	c := &Adaptor{
    61  		name:    gobot.DefaultName("UP2"),
    62  		sys:     sys,
    63  		ledPath: "/sys/class/leds/upboard:%s:/brightness",
    64  		pinmap:  fixedPins,
    65  	}
    66  	c.DigitalPinsAdaptor = adaptors.NewDigitalPinsAdaptor(sys, c.translateDigitalPin, opts...)
    67  	c.PWMPinsAdaptor = adaptors.NewPWMPinsAdaptor(sys, c.translatePWMPin)
    68  	c.I2cBusAdaptor = adaptors.NewI2cBusAdaptor(sys, c.validateI2cBusNumber, defaultI2cBusNumber)
    69  	c.SpiBusAdaptor = adaptors.NewSpiBusAdaptor(sys, c.validateSpiBusNumber, defaultSpiBusNumber, defaultSpiChipNumber,
    70  		defaultSpiMode, defaultSpiBitsNumber, defaultSpiMaxSpeed)
    71  	return c
    72  }
    73  
    74  // Name returns the name of the Adaptor
    75  func (c *Adaptor) Name() string { return c.name }
    76  
    77  // SetName sets the name of the Adaptor
    78  func (c *Adaptor) SetName(n string) { c.name = n }
    79  
    80  // Connect create new connection to board and pins.
    81  func (c *Adaptor) Connect() error {
    82  	c.mutex.Lock()
    83  	defer c.mutex.Unlock()
    84  
    85  	if err := c.SpiBusAdaptor.Connect(); err != nil {
    86  		return err
    87  	}
    88  
    89  	if err := c.I2cBusAdaptor.Connect(); err != nil {
    90  		return err
    91  	}
    92  
    93  	if err := c.PWMPinsAdaptor.Connect(); err != nil {
    94  		return err
    95  	}
    96  	return c.DigitalPinsAdaptor.Connect()
    97  }
    98  
    99  // Finalize closes connection to board and pins
   100  func (c *Adaptor) Finalize() error {
   101  	c.mutex.Lock()
   102  	defer c.mutex.Unlock()
   103  
   104  	err := c.DigitalPinsAdaptor.Finalize()
   105  
   106  	if e := c.PWMPinsAdaptor.Finalize(); e != nil {
   107  		err = multierror.Append(err, e)
   108  	}
   109  
   110  	if e := c.I2cBusAdaptor.Finalize(); e != nil {
   111  		err = multierror.Append(err, e)
   112  	}
   113  
   114  	if e := c.SpiBusAdaptor.Finalize(); e != nil {
   115  		err = multierror.Append(err, e)
   116  	}
   117  	return err
   118  }
   119  
   120  // DigitalWrite writes digital value to the specified pin.
   121  func (c *Adaptor) DigitalWrite(id string, val byte) error {
   122  	c.mutex.Lock()
   123  	defer c.mutex.Unlock()
   124  
   125  	// is it one of the built-in LEDs?
   126  	if id == LEDRed || id == LEDBlue || id == LEDGreen || id == LEDYellow {
   127  		pinPath := fmt.Sprintf(c.ledPath, id)
   128  		fi, err := c.sys.OpenFile(pinPath, os.O_WRONLY|os.O_APPEND, 0666)
   129  		defer fi.Close() //nolint:staticcheck // for historical reasons
   130  		if err != nil {
   131  			return err
   132  		}
   133  		_, err = fi.WriteString(strconv.Itoa(int(val)))
   134  		return err
   135  	}
   136  
   137  	return c.DigitalPinsAdaptor.DigitalWrite(id, val)
   138  }
   139  
   140  func (c *Adaptor) validateSpiBusNumber(busNr int) error {
   141  	// Valid bus numbers are [0,1] which corresponds to /dev/spidev0.x through /dev/spidev1.x.
   142  	// x is the chip number <255
   143  	if (busNr < 0) || (busNr > 1) {
   144  		return fmt.Errorf("Bus number %d out of range", busNr)
   145  	}
   146  	return nil
   147  }
   148  
   149  func (c *Adaptor) validateI2cBusNumber(busNr int) error {
   150  	// Valid bus number is [5..6] which corresponds to /dev/i2c-5 through /dev/i2c-6.
   151  	if (busNr < 5) || (busNr > 6) {
   152  		return fmt.Errorf("Bus number %d out of range", busNr)
   153  	}
   154  	return nil
   155  }
   156  
   157  func (c *Adaptor) translateDigitalPin(id string) (string, int, error) {
   158  	if val, ok := c.pinmap[id]; ok {
   159  		return "", val.pin, nil
   160  	}
   161  	return "", -1, fmt.Errorf("'%s' is not a valid id for a digital pin", id)
   162  }
   163  
   164  func (c *Adaptor) translatePWMPin(id string) (string, int, error) {
   165  	sysPin, ok := c.pinmap[id]
   166  	if !ok {
   167  		return "", -1, fmt.Errorf("'%s' is not a valid id for a pin", id)
   168  	}
   169  	if sysPin.pwmPin == -1 {
   170  		return "", -1, fmt.Errorf("'%s' is not a valid id for a PWM pin", id)
   171  	}
   172  	return "/sys/class/pwm/pwmchip0", sysPin.pwmPin, nil
   173  
   174  }