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