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 }