gobot.io/x/gobot@v1.16.0/drivers/i2c/adafruit_driver.go (about) 1 package i2c 2 3 import ( 4 "errors" 5 "log" 6 "math" 7 "time" 8 9 "gobot.io/x/gobot" 10 ) 11 12 // AdafruitDirection declares a type for specification of the motor direction 13 type AdafruitDirection int 14 15 // AdafruitStepStyle declares a type for specification of the stepper motor rotation 16 type AdafruitStepStyle int 17 18 type adaFruitDCMotor struct { 19 pwmPin, in1Pin, in2Pin byte 20 } 21 type adaFruitStepperMotor struct { 22 pwmPinA, pwmPinB byte 23 ain1, ain2 byte 24 bin1, bin2 byte 25 secPerStep float64 26 currentStep, stepCounter, revSteps int 27 } 28 29 // AdafruitMotorHatDriver is a driver for the DC+Stepper Motor HAT from Adafruit. 30 // The HAT is a Raspberry Pi add-on that can drive up to 4 DC or 2 Stepper motors 31 // with full PWM speed control. It has a dedicated PWM driver chip onboard to 32 // control both motor direction and speed over I2C. 33 type AdafruitMotorHatDriver struct { 34 name string 35 connector Connector 36 motorHatConnection Connection 37 servoHatConnection Connection 38 Config 39 gobot.Commander 40 dcMotors []adaFruitDCMotor 41 stepperMotors []adaFruitStepperMotor 42 } 43 44 var adafruitDebug = false // Set this to true to see debug output 45 46 var ( 47 // Each Adafruit HAT must have a unique I2C address. The default address for 48 // the DC and Stepper Motor HAT is 0x60. The addresses of the Motor HATs can 49 // range from 0x60 to 0x80 for a total of 32 unique addresses. 50 // The base address for the Adafruit PWM-Servo HAT is 0x40. Please consult 51 // the Adafruit documentation for soldering and addressing stacked HATs. 52 motorHatAddress = 0x60 53 servoHatAddress = 0x40 54 stepperMicrosteps = 8 55 stepperMicrostepCurve []int 56 step2coils = make(map[int][]int32) 57 ) 58 59 const ( 60 // Registers 61 _Mode1 = 0x00 62 _Mode2 = 0x01 63 _SubAdr1 = 0x02 64 _SubAdr2 = 0x03 65 _SubAdr3 = 0x04 66 _Prescale = 0xFE 67 _LedZeroOnL = 0x06 68 _LedZeroOnH = 0x07 69 _LedZeroOffL = 0x08 70 _LedZeroOffH = 0x09 71 _AllLedOnL = 0xFA 72 _AllLedOnH = 0xFB 73 _AllLedOffL = 0xFC 74 _AllLedOffH = 0xFD 75 76 // Bits 77 _Restart = 0x80 78 _Sleep = 0x10 79 _AllCall = 0x01 80 _Invrt = 0x10 81 _Outdrv = 0x04 82 ) 83 84 const ( 85 AdafruitForward AdafruitDirection = iota // 0 86 AdafruitBackward // 1 87 AdafruitRelease // 2 88 ) 89 90 const ( 91 AdafruitSingle AdafruitStepStyle = iota // 0 92 AdafruitDouble // 1 93 AdafruitInterleave // 2 94 AdafruitMicrostep // 3 95 ) 96 97 // NewAdafruitMotorHatDriver initializes the internal DCMotor and StepperMotor types. 98 // Again the Adafruit Motor Hat supports up to four DC motors and up to two stepper motors. 99 // Params: 100 // conn Connector - the Adaptor to use with this Driver 101 // 102 // Optional params: 103 // i2c.WithBus(int): bus to use with this driver 104 // i2c.WithAddress(int): address to use with this driver 105 // 106 func NewAdafruitMotorHatDriver(conn Connector, options ...func(Config)) *AdafruitMotorHatDriver { 107 var dc []adaFruitDCMotor 108 var st []adaFruitStepperMotor 109 for i := 0; i < 4; i++ { 110 switch { 111 case i == 0: 112 dc = append(dc, adaFruitDCMotor{pwmPin: 8, in1Pin: 10, in2Pin: 9}) 113 st = append(st, adaFruitStepperMotor{pwmPinA: 8, pwmPinB: 13, 114 ain1: 10, ain2: 9, bin1: 11, bin2: 12, revSteps: 200, secPerStep: 0.1}) 115 case i == 1: 116 dc = append(dc, adaFruitDCMotor{pwmPin: 13, in1Pin: 11, in2Pin: 12}) 117 st = append(st, adaFruitStepperMotor{pwmPinA: 2, pwmPinB: 7, 118 ain1: 4, ain2: 3, bin1: 5, bin2: 6, revSteps: 200, secPerStep: 0.1}) 119 case i == 2: 120 dc = append(dc, adaFruitDCMotor{pwmPin: 2, in1Pin: 4, in2Pin: 3}) 121 case i == 3: 122 dc = append(dc, adaFruitDCMotor{pwmPin: 7, in1Pin: 5, in2Pin: 6}) 123 } 124 } 125 126 driver := &AdafruitMotorHatDriver{ 127 name: gobot.DefaultName("AdafruitMotorHat"), 128 connector: conn, 129 Config: NewConfig(), 130 Commander: gobot.NewCommander(), 131 dcMotors: dc, 132 stepperMotors: st, 133 } 134 135 for _, option := range options { 136 option(driver) 137 } 138 139 // TODO: add API funcs 140 return driver 141 } 142 143 // SetMotorHatAddress sets the I2C address for the DC and Stepper Motor HAT. 144 // This addressing flexibility empowers "stacking" the HATs. 145 func (a *AdafruitMotorHatDriver) SetMotorHatAddress(addr int) (err error) { 146 motorHatAddress = addr 147 return 148 } 149 150 // SetServoHatAddress sets the I2C address for the PWM-Servo Motor HAT. 151 // This addressing flexibility empowers "stacking" the HATs. 152 func (a *AdafruitMotorHatDriver) SetServoHatAddress(addr int) (err error) { 153 servoHatAddress = addr 154 return 155 } 156 157 // Name identifies this driver object 158 func (a *AdafruitMotorHatDriver) Name() string { return a.name } 159 160 // SetName sets nae for driver 161 func (a *AdafruitMotorHatDriver) SetName(n string) { a.name = n } 162 163 // Connection identifies the particular adapter object 164 func (a *AdafruitMotorHatDriver) Connection() gobot.Connection { return a.connector.(gobot.Connection) } 165 166 func (a *AdafruitMotorHatDriver) startDriver(connection Connection) (err error) { 167 if err = a.setAllPWM(connection, 0, 0); err != nil { 168 return 169 } 170 reg := byte(_Mode2) 171 val := byte(_Outdrv) 172 if _, err = connection.Write([]byte{reg, val}); err != nil { 173 return 174 } 175 reg = byte(_Mode1) 176 val = byte(_AllCall) 177 if _, err = connection.Write([]byte{reg, val}); err != nil { 178 return 179 } 180 time.Sleep(5 * time.Millisecond) 181 182 // Read a byte from the I2C device. Note: no ability to read from a specified reg? 183 mode1 := []byte{0} 184 _, rerr := connection.Read(mode1) 185 if rerr != nil { 186 return rerr 187 } 188 if len(mode1) > 0 { 189 reg = byte(_Mode1) 190 val = mode1[0] & _Sleep 191 if _, err = connection.Write([]byte{reg, val}); err != nil { 192 return 193 } 194 time.Sleep(5 * time.Millisecond) 195 } 196 197 return 198 } 199 200 // Start initializes both I2C-addressable Adafruit Motor HAT drivers 201 func (a *AdafruitMotorHatDriver) Start() (err error) { 202 bus := a.GetBusOrDefault(a.connector.GetDefaultBus()) 203 204 err = a.startServoHat(bus) 205 if adafruitDebug && err != nil { 206 log.Printf("[adafruit_driver] start servohat error: %s\n", err) 207 } 208 209 err = a.startMotorHat(bus) 210 if adafruitDebug && err != nil { 211 log.Printf("[adafruit_driver] start motorhat error: %s\n", err) 212 } 213 214 return 215 } 216 217 // startServoHat starts the Servo motors connection. 218 func (a *AdafruitMotorHatDriver) startServoHat(bus int) (err error) { 219 if a.servoHatConnection, err = a.connector.GetConnection(servoHatAddress, bus); err != nil { 220 return 221 } 222 223 if err = a.startDriver(a.servoHatConnection); err != nil { 224 return 225 } 226 227 return 228 } 229 230 // startMotorHat starts the DC motors connection. 231 func (a *AdafruitMotorHatDriver) startMotorHat(bus int) (err error) { 232 if a.motorHatConnection, err = a.connector.GetConnection(motorHatAddress, bus); err != nil { 233 return 234 } 235 236 if err = a.startDriver(a.motorHatConnection); err != nil { 237 return 238 } 239 240 return 241 } 242 243 // Halt returns true if devices is halted successfully 244 func (a *AdafruitMotorHatDriver) Halt() (err error) { return } 245 246 // setPWM sets the start (on) and end (off) of the high-segment of the PWM pulse 247 // on the specific channel (pin). 248 func (a *AdafruitMotorHatDriver) setPWM(conn Connection, pin byte, on, off int32) (err error) { 249 // register and values to be written to that register 250 regVals := make(map[int][]byte) 251 regVals[0] = []byte{byte(_LedZeroOnL + 4*pin), byte(on & 0xff)} 252 regVals[1] = []byte{byte(_LedZeroOnH + 4*pin), byte(on >> 8)} 253 regVals[2] = []byte{byte(_LedZeroOffL + 4*pin), byte(off & 0xff)} 254 regVals[3] = []byte{byte(_LedZeroOffH + 4*pin), byte(off >> 8)} 255 for i := 0; i < len(regVals); i++ { 256 if _, err = conn.Write(regVals[i]); err != nil { 257 return 258 } 259 } 260 return 261 } 262 263 // SetServoMotorFreq sets the frequency for the currently addressed PWM Servo HAT. 264 func (a *AdafruitMotorHatDriver) SetServoMotorFreq(freq float64) (err error) { 265 if err = a.setPWMFreq(a.servoHatConnection, freq); err != nil { 266 return 267 } 268 return 269 } 270 271 // SetServoMotorPulse is a convenience function to specify the 'tick' value, 272 // between 0-4095, when the signal will turn on, and when it will turn off. 273 func (a *AdafruitMotorHatDriver) SetServoMotorPulse(channel byte, on, off int32) (err error) { 274 if err = a.setPWM(a.servoHatConnection, channel, on, off); err != nil { 275 return 276 } 277 return 278 } 279 280 // setPWMFreq adjusts the PWM frequency which determines how many full 281 // pulses per second are generated by the integrated circuit. The frequency 282 // determines how "long" each pulse is in duration from start to finish, 283 // taking into account the high and low segments of the pulse. 284 func (a *AdafruitMotorHatDriver) setPWMFreq(conn Connection, freq float64) (err error) { 285 // 25MHz 286 preScaleVal := 25000000.0 287 // 12-bit 288 preScaleVal /= 4096.0 289 preScaleVal /= freq 290 preScaleVal -= 1.0 291 preScale := math.Floor(preScaleVal + 0.5) 292 if adafruitDebug { 293 log.Printf("Setting PWM frequency to: %.2f Hz", freq) 294 log.Printf("Estimated pre-scale: %.2f", preScaleVal) 295 log.Printf("Final pre-scale: %.2f", preScale) 296 } 297 // default (and only) reads register 0 298 oldMode := []byte{0} 299 _, err = conn.Read(oldMode) 300 if err != nil { 301 return 302 } 303 // sleep? 304 if len(oldMode) > 0 { 305 newMode := (oldMode[0] & 0x7F) | 0x10 306 reg := byte(_Mode1) 307 if _, err = conn.Write([]byte{reg, newMode}); err != nil { 308 return 309 } 310 reg = byte(_Prescale) 311 val := byte(math.Floor(preScale)) 312 if _, err = conn.Write([]byte{reg, val}); err != nil { 313 return 314 } 315 reg = byte(_Mode1) 316 if _, err = conn.Write([]byte{reg, oldMode[0]}); err != nil { 317 return 318 } 319 time.Sleep(5 * time.Millisecond) 320 if _, err = conn.Write([]byte{reg, (oldMode[0] | 0x80)}); err != nil { 321 return 322 } 323 } 324 return 325 } 326 327 // setAllPWM sets all PWM channels for the given address 328 func (a *AdafruitMotorHatDriver) setAllPWM(conn Connection, on, off int32) (err error) { 329 // register and values to be written to that register 330 regVals := make(map[int][]byte) 331 regVals[0] = []byte{byte(_AllLedOnL), byte(on & 0xff)} 332 regVals[1] = []byte{byte(_AllLedOnH), byte(on >> 8)} 333 regVals[2] = []byte{byte(_AllLedOffL), byte(off & 0xFF)} 334 regVals[3] = []byte{byte(_AllLedOffH), byte(off >> 8)} 335 for i := 0; i < len(regVals); i++ { 336 if _, err = conn.Write(regVals[i]); err != nil { 337 return 338 } 339 } 340 return 341 } 342 343 func (a *AdafruitMotorHatDriver) setPin(conn Connection, pin byte, value int32) (err error) { 344 if value == 0 { 345 return a.setPWM(conn, pin, 0, 4096) 346 } 347 if value == 1 { 348 return a.setPWM(conn, pin, 4096, 0) 349 } 350 return errors.New("Invalid pin") 351 } 352 353 // SetDCMotorSpeed will set the appropriate pins to run the specified DC motor 354 // for the given speed. 355 func (a *AdafruitMotorHatDriver) SetDCMotorSpeed(dcMotor int, speed int32) (err error) { 356 if err = a.setPWM(a.motorHatConnection, a.dcMotors[dcMotor].pwmPin, 0, speed*16); err != nil { 357 return 358 } 359 return 360 } 361 362 // RunDCMotor will set the appropriate pins to run the specified DC motor for 363 // the given direction 364 func (a *AdafruitMotorHatDriver) RunDCMotor(dcMotor int, dir AdafruitDirection) (err error) { 365 366 switch { 367 case dir == AdafruitForward: 368 if err = a.setPin(a.motorHatConnection, a.dcMotors[dcMotor].in2Pin, 0); err != nil { 369 return 370 } 371 if err = a.setPin(a.motorHatConnection, a.dcMotors[dcMotor].in1Pin, 1); err != nil { 372 return 373 } 374 case dir == AdafruitBackward: 375 if err = a.setPin(a.motorHatConnection, a.dcMotors[dcMotor].in1Pin, 0); err != nil { 376 return 377 } 378 if err = a.setPin(a.motorHatConnection, a.dcMotors[dcMotor].in2Pin, 1); err != nil { 379 return 380 } 381 case dir == AdafruitRelease: 382 if err = a.setPin(a.motorHatConnection, a.dcMotors[dcMotor].in1Pin, 0); err != nil { 383 return 384 } 385 if err = a.setPin(a.motorHatConnection, a.dcMotors[dcMotor].in2Pin, 0); err != nil { 386 return 387 } 388 } 389 return 390 } 391 392 func (a *AdafruitMotorHatDriver) oneStep(motor int, dir AdafruitDirection, style AdafruitStepStyle) (steps int, err error) { 393 pwmA := 255 394 pwmB := 255 395 396 // Determine the stepping procedure 397 switch { 398 case style == AdafruitSingle: 399 if (a.stepperMotors[motor].currentStep / (stepperMicrosteps / 2) % 2) != 0 { 400 // we're at an odd step 401 if dir == AdafruitForward { 402 a.stepperMotors[motor].currentStep += stepperMicrosteps / 2 403 } else { 404 a.stepperMotors[motor].currentStep -= stepperMicrosteps / 2 405 } 406 } else { 407 // go to next even step 408 if dir == AdafruitForward { 409 a.stepperMotors[motor].currentStep += stepperMicrosteps 410 } else { 411 a.stepperMotors[motor].currentStep -= stepperMicrosteps 412 } 413 } 414 case style == AdafruitDouble: 415 if (a.stepperMotors[motor].currentStep / (stepperMicrosteps / 2) % 2) == 0 { 416 // we're at an even step, weird 417 if dir == AdafruitForward { 418 a.stepperMotors[motor].currentStep += stepperMicrosteps / 2 419 } else { 420 a.stepperMotors[motor].currentStep -= stepperMicrosteps / 2 421 } 422 } else { 423 // go to next odd step 424 if dir == AdafruitForward { 425 a.stepperMotors[motor].currentStep += stepperMicrosteps 426 } else { 427 a.stepperMotors[motor].currentStep -= stepperMicrosteps 428 } 429 } 430 case style == AdafruitInterleave: 431 if dir == AdafruitForward { 432 a.stepperMotors[motor].currentStep += stepperMicrosteps / 2 433 } else { 434 a.stepperMotors[motor].currentStep -= stepperMicrosteps / 2 435 } 436 case style == AdafruitMicrostep: 437 if dir == AdafruitForward { 438 a.stepperMotors[motor].currentStep++ 439 } else { 440 a.stepperMotors[motor].currentStep-- 441 } 442 // go to next step and wrap around 443 a.stepperMotors[motor].currentStep += stepperMicrosteps * 4 444 a.stepperMotors[motor].currentStep %= stepperMicrosteps * 4 445 446 pwmA = 0 447 pwmB = 0 448 currStep := a.stepperMotors[motor].currentStep 449 if currStep >= 0 && currStep < stepperMicrosteps { 450 pwmA = stepperMicrostepCurve[stepperMicrosteps-currStep] 451 pwmB = stepperMicrostepCurve[currStep] 452 } else if currStep >= stepperMicrosteps && currStep < stepperMicrosteps*2 { 453 pwmA = stepperMicrostepCurve[currStep-stepperMicrosteps] 454 pwmB = stepperMicrostepCurve[stepperMicrosteps*2-currStep] 455 } else if currStep >= stepperMicrosteps*2 && currStep < stepperMicrosteps*3 { 456 pwmA = stepperMicrostepCurve[stepperMicrosteps*3-currStep] 457 pwmB = stepperMicrostepCurve[currStep-stepperMicrosteps*2] 458 } else if currStep >= stepperMicrosteps*3 && currStep < stepperMicrosteps*4 { 459 pwmA = stepperMicrostepCurve[currStep-stepperMicrosteps*3] 460 pwmB = stepperMicrostepCurve[stepperMicrosteps*4-currStep] 461 } 462 } //switch 463 464 //go to next 'step' and wrap around 465 a.stepperMotors[motor].currentStep += stepperMicrosteps * 4 466 a.stepperMotors[motor].currentStep %= stepperMicrosteps * 4 467 468 //only really used for microstepping, otherwise always on! 469 if err = a.setPWM(a.motorHatConnection, a.stepperMotors[motor].pwmPinA, 0, int32(pwmA*16)); err != nil { 470 return 471 } 472 if err = a.setPWM(a.motorHatConnection, a.stepperMotors[motor].pwmPinB, 0, int32(pwmB*16)); err != nil { 473 return 474 } 475 var coils []int32 476 currStep := a.stepperMotors[motor].currentStep 477 if style == AdafruitMicrostep { 478 switch { 479 case currStep >= 0 && currStep < stepperMicrosteps: 480 coils = []int32{1, 1, 0, 0} 481 case currStep >= stepperMicrosteps && currStep < stepperMicrosteps*2: 482 coils = []int32{0, 1, 1, 0} 483 case currStep >= stepperMicrosteps*2 && currStep < stepperMicrosteps*3: 484 coils = []int32{0, 0, 1, 1} 485 case currStep >= stepperMicrosteps*3 && currStep < stepperMicrosteps*4: 486 coils = []int32{1, 0, 0, 1} 487 } 488 } else { 489 // step-2-coils is initialized in init() 490 coils = step2coils[(currStep / (stepperMicrosteps / 2))] 491 } 492 if adafruitDebug { 493 log.Printf("[adafruit_driver] currStep: %d, index into step2coils: %d\n", 494 currStep, (currStep / (stepperMicrosteps / 2))) 495 log.Printf("[adafruit_driver] coils state = %v", coils) 496 } 497 if err = a.setPin(a.motorHatConnection, a.stepperMotors[motor].ain2, coils[0]); err != nil { 498 return 499 } 500 if err = a.setPin(a.motorHatConnection, a.stepperMotors[motor].bin1, coils[1]); err != nil { 501 return 502 } 503 if err = a.setPin(a.motorHatConnection, a.stepperMotors[motor].ain1, coils[2]); err != nil { 504 return 505 } 506 if err = a.setPin(a.motorHatConnection, a.stepperMotors[motor].bin2, coils[3]); err != nil { 507 return 508 } 509 return a.stepperMotors[motor].currentStep, nil 510 } 511 512 // SetStepperMotorSpeed sets the seconds-per-step for the given Stepper Motor. 513 func (a *AdafruitMotorHatDriver) SetStepperMotorSpeed(stepperMotor int, rpm int) (err error) { 514 revSteps := a.stepperMotors[stepperMotor].revSteps 515 a.stepperMotors[stepperMotor].secPerStep = 60.0 / float64(revSteps*rpm) 516 a.stepperMotors[stepperMotor].stepCounter = 0 517 return 518 } 519 520 // Step will rotate the stepper motor the given number of steps, in the given direction and step style. 521 func (a *AdafruitMotorHatDriver) Step(motor, steps int, dir AdafruitDirection, style AdafruitStepStyle) (err error) { 522 secPerStep := a.stepperMotors[motor].secPerStep 523 latestStep := 0 524 if style == AdafruitInterleave { 525 secPerStep = secPerStep / 2.0 526 } 527 if style == AdafruitMicrostep { 528 secPerStep /= float64(stepperMicrosteps) 529 steps *= stepperMicrosteps 530 } 531 if adafruitDebug { 532 log.Printf("[adafruit_driver] %f seconds per step", secPerStep) 533 } 534 for i := 0; i < steps; i++ { 535 if latestStep, err = a.oneStep(motor, dir, style); err != nil { 536 return 537 } 538 time.Sleep(time.Duration(secPerStep) * time.Second) 539 } 540 // As documented in the Adafruit python driver: 541 // This is an edge case, if we are in between full steps, keep going to end on a full step 542 if style == AdafruitMicrostep { 543 for latestStep != 0 && latestStep != stepperMicrosteps { 544 if latestStep, err = a.oneStep(motor, dir, style); err != nil { 545 return 546 } 547 time.Sleep(time.Duration(secPerStep) * time.Second) 548 } 549 } 550 return 551 } 552 553 func init() { 554 stepperMicrostepCurve = []int{0, 50, 98, 142, 180, 212, 236, 250, 255} 555 step2coils[0] = []int32{1, 0, 0, 0} 556 step2coils[1] = []int32{1, 1, 0, 0} 557 step2coils[2] = []int32{0, 1, 0, 0} 558 step2coils[3] = []int32{0, 1, 1, 0} 559 step2coils[4] = []int32{0, 0, 1, 0} 560 step2coils[5] = []int32{0, 0, 1, 1} 561 step2coils[6] = []int32{0, 0, 0, 1} 562 step2coils[7] = []int32{1, 0, 0, 1} 563 }