gobot.io/x/gobot@v1.16.0/platforms/intel-iot/edison/edison_adaptor.go (about) 1 package edison 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "strconv" 8 "sync" 9 10 multierror "github.com/hashicorp/go-multierror" 11 "gobot.io/x/gobot" 12 "gobot.io/x/gobot/drivers/i2c" 13 "gobot.io/x/gobot/sysfs" 14 ) 15 16 type mux struct { 17 pin int 18 value int 19 } 20 21 type sysfsPin struct { 22 pin int 23 resistor int 24 levelShifter int 25 pwmPin int 26 mux []mux 27 } 28 29 // Adaptor represents a Gobot Adaptor for an Intel Edison 30 type Adaptor struct { 31 name string 32 board string 33 pinmap map[string]sysfsPin 34 tristate *sysfs.DigitalPin 35 digitalPins map[int]*sysfs.DigitalPin 36 pwmPins map[int]*sysfs.PWMPin 37 i2cBus i2c.I2cDevice 38 connect func(e *Adaptor) (err error) 39 writeFile func(path string, data []byte) (i int, err error) 40 readFile func(path string) ([]byte, error) 41 mutex *sync.Mutex 42 } 43 44 // NewAdaptor returns a new Edison Adaptor 45 func NewAdaptor() *Adaptor { 46 return &Adaptor{ 47 name: gobot.DefaultName("Edison"), 48 pinmap: arduinoPinMap, 49 writeFile: writeFile, 50 readFile: readFile, 51 mutex: &sync.Mutex{}, 52 } 53 } 54 55 // Name returns the Adaptors name 56 func (e *Adaptor) Name() string { return e.name } 57 58 // SetName sets the Adaptors name 59 func (e *Adaptor) SetName(n string) { e.name = n } 60 61 // Board returns the Adaptors board name 62 func (e *Adaptor) Board() string { return e.board } 63 64 // SetBoard sets the Adaptors name 65 func (e *Adaptor) SetBoard(n string) { e.board = n } 66 67 // Connect initializes the Edison for use with the Arduino beakout board 68 func (e *Adaptor) Connect() (err error) { 69 e.digitalPins = make(map[int]*sysfs.DigitalPin) 70 e.pwmPins = make(map[int]*sysfs.PWMPin) 71 72 if e.Board() == "arduino" || e.Board() == "" { 73 aerr := e.checkForArduino() 74 if aerr != nil { 75 return aerr 76 } 77 e.board = "arduino" 78 } 79 80 switch e.Board() { 81 case "sparkfun": 82 e.pinmap = sparkfunPinMap 83 e.sparkfunSetup() 84 case "arduino": 85 e.pinmap = arduinoPinMap 86 if errs := e.arduinoSetup(); errs != nil { 87 err = multierror.Append(err, errs) 88 } 89 case "miniboard": 90 e.pinmap = miniboardPinMap 91 e.miniboardSetup() 92 default: 93 errs := errors.New("Unknown board type: " + e.Board()) 94 err = multierror.Append(err, errs) 95 } 96 return 97 } 98 99 // Finalize releases all i2c devices and exported analog, digital, pwm pins. 100 func (e *Adaptor) Finalize() (err error) { 101 if errs := e.tristate.Unexport(); errs != nil { 102 err = multierror.Append(err, errs) 103 } 104 for _, pin := range e.digitalPins { 105 if pin != nil { 106 if errs := pin.Unexport(); errs != nil { 107 err = multierror.Append(err, errs) 108 } 109 } 110 } 111 for _, pin := range e.pwmPins { 112 if pin != nil { 113 if errs := pin.Enable(false); errs != nil { 114 err = multierror.Append(err, errs) 115 } 116 if errs := pin.Unexport(); errs != nil { 117 err = multierror.Append(err, errs) 118 } 119 } 120 } 121 if e.i2cBus != nil { 122 if errs := e.i2cBus.Close(); errs != nil { 123 err = multierror.Append(err, errs) 124 } 125 } 126 return 127 } 128 129 // DigitalRead reads digital value from pin 130 func (e *Adaptor) DigitalRead(pin string) (i int, err error) { 131 sysfsPin, err := e.DigitalPin(pin, "in") 132 if err != nil { 133 return 134 } 135 return sysfsPin.Read() 136 } 137 138 // DigitalWrite writes a value to the pin. Acceptable values are 1 or 0. 139 func (e *Adaptor) DigitalWrite(pin string, val byte) (err error) { 140 sysfsPin, err := e.DigitalPin(pin, "out") 141 if err != nil { 142 return 143 } 144 return sysfsPin.Write(int(val)) 145 } 146 147 // PwmWrite writes the 0-254 value to the specified pin 148 func (e *Adaptor) PwmWrite(pin string, val byte) (err error) { 149 pwmPin, err := e.PWMPin(pin) 150 if err != nil { 151 return 152 } 153 period, err := pwmPin.Period() 154 if err != nil { 155 return err 156 } 157 duty := gobot.FromScale(float64(val), 0, 255.0) 158 return pwmPin.SetDutyCycle(uint32(float64(period) * duty)) 159 } 160 161 // AnalogRead returns value from analog reading of specified pin 162 func (e *Adaptor) AnalogRead(pin string) (val int, err error) { 163 buf, err := e.readFile( 164 "/sys/bus/iio/devices/iio:device1/in_voltage" + pin + "_raw", 165 ) 166 if err != nil { 167 return 168 } 169 170 val, err = strconv.Atoi(string(buf[0 : len(buf)-1])) 171 172 return val / 4, err 173 } 174 175 // GetConnection returns an i2c connection to a device on a specified bus. 176 // Valid bus numbers are 1 and 6 (arduino). 177 func (e *Adaptor) GetConnection(address int, bus int) (connection i2c.Connection, err error) { 178 e.mutex.Lock() 179 defer e.mutex.Unlock() 180 181 if !(bus == e.GetDefaultBus()) { 182 return nil, errors.New("Unsupported I2C bus") 183 } 184 if e.i2cBus == nil { 185 if bus == 6 && e.board == "arduino" { 186 e.arduinoI2CSetup() 187 } 188 e.i2cBus, err = sysfs.NewI2cDevice(fmt.Sprintf("/dev/i2c-%d", bus)) 189 } 190 return i2c.NewConnection(e.i2cBus, address), err 191 } 192 193 // GetDefaultBus returns the default i2c bus for this platform 194 func (e *Adaptor) GetDefaultBus() int { 195 // Arduino uses bus 6 196 if e.board == "arduino" { 197 return 6 198 } 199 200 return 1 201 } 202 203 // DigitalPin returns matched sysfs.DigitalPin for specified values 204 func (e *Adaptor) DigitalPin(pin string, dir string) (sysfsPin sysfs.DigitalPinner, err error) { 205 e.mutex.Lock() 206 defer e.mutex.Unlock() 207 208 i := e.pinmap[pin] 209 if e.digitalPins[i.pin] == nil { 210 e.digitalPins[i.pin], err = e.newExportedPin(i.pin) 211 if err != nil { 212 return 213 } 214 215 if i.resistor > 0 { 216 e.digitalPins[i.resistor], err = e.newExportedPin(i.resistor) 217 if err != nil { 218 return 219 } 220 } 221 222 if i.levelShifter > 0 { 223 e.digitalPins[i.levelShifter], err = e.newExportedPin(i.levelShifter) 224 if err != nil { 225 return 226 } 227 } 228 229 if len(i.mux) > 0 { 230 for _, mux := range i.mux { 231 e.digitalPins[mux.pin], err = e.newExportedPin(mux.pin) 232 if err != nil { 233 return 234 } 235 236 err = pinWrite(e.digitalPins[mux.pin], sysfs.OUT, mux.value) 237 if err != nil { 238 return 239 } 240 } 241 } 242 } 243 244 if dir == "in" { 245 if err = e.digitalPins[i.pin].Direction(sysfs.IN); err != nil { 246 return 247 } 248 249 if i.resistor > 0 { 250 err = pinWrite(e.digitalPins[i.resistor], sysfs.OUT, sysfs.LOW) 251 if err != nil { 252 return 253 } 254 } 255 256 if i.levelShifter > 0 { 257 err = pinWrite(e.digitalPins[i.levelShifter], sysfs.OUT, sysfs.LOW) 258 if err != nil { 259 return 260 } 261 } 262 } else if dir == "out" { 263 if err = e.digitalPins[i.pin].Direction(sysfs.OUT); err != nil { 264 return 265 } 266 267 if i.resistor > 0 { 268 if err = e.digitalPins[i.resistor].Direction(sysfs.IN); err != nil { 269 return 270 } 271 } 272 273 if i.levelShifter > 0 { 274 err = pinWrite(e.digitalPins[i.levelShifter], sysfs.OUT, sysfs.HIGH) 275 if err != nil { 276 return 277 } 278 } 279 } 280 return e.digitalPins[i.pin], nil 281 } 282 283 // PWMPin returns a sysfs.PWMPin 284 func (e *Adaptor) PWMPin(pin string) (sysfsPin sysfs.PWMPinner, err error) { 285 sysPin := e.pinmap[pin] 286 if sysPin.pwmPin != -1 { 287 if e.pwmPins[sysPin.pwmPin] == nil { 288 if err = e.DigitalWrite(pin, 1); err != nil { 289 return 290 } 291 if err = changePinMode(e, strconv.Itoa(int(sysPin.pin)), "1"); err != nil { 292 return 293 } 294 e.mutex.Lock() 295 defer e.mutex.Unlock() 296 297 e.pwmPins[sysPin.pwmPin] = sysfs.NewPWMPin(sysPin.pwmPin) 298 if err = e.pwmPins[sysPin.pwmPin].Export(); err != nil { 299 return 300 } 301 if err = e.pwmPins[sysPin.pwmPin].Enable(true); err != nil { 302 return 303 } 304 } 305 306 sysfsPin = e.pwmPins[sysPin.pwmPin] 307 return 308 } 309 err = errors.New("Not a PWM pin") 310 return 311 } 312 313 // TODO: also check to see if device labels for 314 // /sys/class/gpio/gpiochip{200,216,232,248}/label == "pcal9555a" 315 func (e *Adaptor) checkForArduino() error { 316 if err := e.exportTristatePin(); err != nil { 317 return err 318 } 319 return nil 320 } 321 322 func (e *Adaptor) newExportedPin(pin int) (sysfsPin *sysfs.DigitalPin, err error) { 323 sysfsPin = sysfs.NewDigitalPin(pin) 324 err = sysfsPin.Export() 325 return 326 } 327 328 func (e *Adaptor) exportTristatePin() (err error) { 329 e.tristate, err = e.newExportedPin(214) 330 return 331 } 332 333 // arduinoSetup does needed setup for the Arduino compatible breakout board 334 func (e *Adaptor) arduinoSetup() (err error) { 335 if err = e.exportTristatePin(); err != nil { 336 return err 337 } 338 339 err = pinWrite(e.tristate, sysfs.OUT, sysfs.LOW) 340 if err != nil { 341 return 342 } 343 344 for _, i := range []int{263, 262} { 345 if err = e.newDigitalPin(i, sysfs.HIGH); err != nil { 346 return err 347 } 348 } 349 350 for _, i := range []int{240, 241, 242, 243} { 351 if err = e.newDigitalPin(i, sysfs.LOW); err != nil { 352 return err 353 } 354 } 355 356 for _, i := range []string{"111", "115", "114", "109"} { 357 if err = changePinMode(e, i, "1"); err != nil { 358 return err 359 } 360 } 361 362 for _, i := range []string{"131", "129", "40"} { 363 if err = changePinMode(e, i, "0"); err != nil { 364 return err 365 } 366 } 367 368 err = e.tristate.Write(sysfs.HIGH) 369 return 370 } 371 372 func (e *Adaptor) arduinoI2CSetup() (err error) { 373 if err = e.tristate.Write(sysfs.LOW); err != nil { 374 return 375 } 376 377 for _, i := range []int{14, 165, 212, 213} { 378 io := sysfs.NewDigitalPin(i) 379 if err = io.Export(); err != nil { 380 return 381 } 382 if err = io.Direction(sysfs.IN); err != nil { 383 return 384 } 385 if err = io.Unexport(); err != nil { 386 return 387 } 388 } 389 390 for _, i := range []int{236, 237, 204, 205} { 391 if err = e.newDigitalPin(i, sysfs.LOW); err != nil { 392 return err 393 } 394 } 395 396 for _, i := range []string{"28", "27"} { 397 if err = changePinMode(e, i, "1"); err != nil { 398 return 399 } 400 } 401 402 if err = e.tristate.Write(sysfs.HIGH); err != nil { 403 return 404 } 405 406 return 407 } 408 409 func (e *Adaptor) sparkfunSetup() (err error) { 410 return 411 } 412 413 // miniboardSetup does needed setup for Edison minibpard and other compatible boards 414 func (e *Adaptor) miniboardSetup() (err error) { 415 return 416 } 417 418 func (e *Adaptor) newDigitalPin(i int, level int) (err error) { 419 io := sysfs.NewDigitalPin(i) 420 if err = io.Export(); err != nil { 421 return 422 } 423 if err = io.Direction(sysfs.OUT); err != nil { 424 return 425 } 426 if err = io.Write(level); err != nil { 427 return 428 } 429 err = io.Unexport() 430 return 431 } 432 433 func writeFile(path string, data []byte) (i int, err error) { 434 file, err := sysfs.OpenFile(path, os.O_WRONLY, 0644) 435 defer file.Close() 436 if err != nil { 437 return 438 } 439 440 return file.Write(data) 441 } 442 443 func readFile(path string) ([]byte, error) { 444 file, err := sysfs.OpenFile(path, os.O_RDONLY, 0644) 445 defer file.Close() 446 if err != nil { 447 return make([]byte, 0), err 448 } 449 450 buf := make([]byte, 200) 451 var i int 452 i, err = file.Read(buf) 453 if i == 0 { 454 return buf, err 455 } 456 return buf[:i], err 457 } 458 459 // changePinMode writes pin mode to current_pinmux file 460 func changePinMode(a *Adaptor, pin, mode string) (err error) { 461 _, err = a.writeFile( 462 "/sys/kernel/debug/gpio_debug/gpio"+pin+"/current_pinmux", 463 []byte("mode"+mode), 464 ) 465 return 466 } 467 468 // pinWrite sets Direction and writes level for a specific pin 469 func pinWrite(pin *sysfs.DigitalPin, dir string, level int) (err error) { 470 if err = pin.Direction(dir); err != nil { 471 return 472 } 473 474 err = pin.Write(level) 475 return 476 }