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 }