Source code for adafruit_motor_hat.MotorHat

# -*- coding: utf-8 -*-
from __future__ import division

import time

from Adafruit_PWM_Servo_Driver import PWM


# This code is from https://github.com/adafruit/Adafruit-Motor-HAT-Python-Library
# It is licensed under MIT, please see the licence docs


[docs]class AdafruitStepperMotor(object): MICROSTEPS = 8 MICROSTEP_CURVE = [0, 50, 98, 142, 180, 212, 236, 250, 255] # MICROSTEPS = 16 # a sinusoidal curve NOT LINEAR! # MICROSTEP_CURVE = [0, 25, 50, 74, 98, 120, 141, 162, 180, 197, 212, 225, 236, 244, 250, 253, 255] def __init__(self, controller, num, steps=200): """ Initialize the Stepper Motor object :param controller: :param num: :param steps: the number of steps corresponding to a full rotation of the stepper """ self.pwm_a = 255 self.pwm_b = 255 self.MC = controller self.revsteps = steps self.motornum = num self.sec_per_step = 0.1 self.stepping_counter = 0 self.currentstep = 0 num -= 1 if num == 0: self.PWMA = 8 self.AIN2 = 9 self.AIN1 = 10 self.PWMB = 13 self.BIN2 = 12 self.BIN1 = 11 elif num == 1: self.PWMA = 2 self.AIN2 = 3 self.AIN1 = 4 self.PWMB = 7 self.BIN2 = 6 self.BIN1 = 5 else: raise ValueError('MotorHAT Stepper must be between 1 and 2 inclusive')
[docs] def set_speed(self, rpm): """ Set the target motor speed in rpm, motor step count is used to calculate the step delay :param rpm: Target RPM :type rpm: float :return: None """ self.sec_per_step = 60.0 / (self.revsteps * rpm) self.stepping_counter = 0
def _step(self, microstep=False): """ Internal step execution function :param microstep: Is this cleaning up for a microstep? :type microstep: bool :return: The current step """ # go to next 'step' and wrap around self.currentstep += self.MICROSTEPS * 4 self.currentstep %= self.MICROSTEPS * 4 # only really used for microstepping, otherwise always on! self.MC._pwm.setPWM(self.PWMA, 0, self.pwm_a * 16) self.MC._pwm.setPWM(self.PWMB, 0, self.pwm_b * 16) # set up coil energizing! coils = [0, 0, 0, 0] if microstep: if (self.currentstep >= 0) and (self.currentstep < self.MICROSTEPS): coils = [1, 1, 0, 0] elif (self.currentstep >= self.MICROSTEPS) and (self.currentstep < self.MICROSTEPS * 2): coils = [0, 1, 1, 0] elif (self.currentstep >= self.MICROSTEPS * 2) and (self.currentstep < self.MICROSTEPS * 3): coils = [0, 0, 1, 1] elif (self.currentstep >= self.MICROSTEPS * 3) and (self.currentstep < self.MICROSTEPS * 4): coils = [1, 0, 0, 1] else: step2coils = [ [1, 0, 0, 0], [1, 1, 0, 0], [0, 1, 0, 0], [0, 1, 1, 0], [0, 0, 1, 0], [0, 0, 1, 1], [0, 0, 0, 1], [1, 0, 0, 1] ] coils = step2coils[self.currentstep // (self.MICROSTEPS // 2)] self.MC.set_pin(self.AIN2, coils[0]) self.MC.set_pin(self.BIN1, coils[1]) self.MC.set_pin(self.AIN1, coils[2]) self.MC.set_pin(self.BIN2, coils[3]) return self.currentstep
[docs] def step(self, steps=1, reverse=False): """ Simple single step :param steps: The number of steps to execute :type steps: int :param reverse: step in reverse :type reverse: Bool :return: None """ s_per_s = self.sec_per_step self.pwm_a = 255 self.pwm_b = 255 for _ in range(steps): if (self.currentstep // (self.MICROSTEPS // 2)) % 2: # we're at an odd step, weird if reverse: self.currentstep -= self.MICROSTEPS // 2 else: self.currentstep += self.MICROSTEPS // 2 else: # go to next even step if reverse: self.currentstep -= self.MICROSTEPS else: self.currentstep += self.MICROSTEPS self._step() time.sleep(s_per_s)
[docs] def double_step(self, steps=1, reverse=False): """ Double stepping, 2 coils at once :param steps: The number of steps to execute :type steps: int :param reverse: step in reverse :type reverse: bool :return: None """ s_per_s = self.sec_per_step self.pwm_a = 255 self.pwm_b = 255 for _ in range(steps): if not (self.currentstep // (self.MICROSTEPS // 2) % 2): # we're at an even step, weird if reverse: self.currentstep -= self.MICROSTEPS // 2 else: self.currentstep += self.MICROSTEPS // 2 else: # go to next odd step if reverse: self.currentstep -= self.MICROSTEPS else: self.currentstep += self.MICROSTEPS self._step() time.sleep(s_per_s)
[docs] def interleaved_step(self, steps=1, reverse=False): """ Interleaved stepping, single stepping and interleaved stepping :param steps: The number of steps to execute :type steps: int :param reverse: step in reverse :type reverse: bool :return: None """ s_per_s = self.sec_per_step / 2.0 self.pwm_a = 255 self.pwm_b = 255 for _ in range(steps): if reverse: self.currentstep -= self.MICROSTEPS // 2 else: self.currentstep += self.MICROSTEPS // 2 self._step() time.sleep(s_per_s)
[docs] def micro_step(self, steps=1, reverse=False): """ Microstepping :param steps: The number of steps to execute :type steps: int :param reverse: step in reverse :type reverse: bool :return: None """ s_per_s = self.sec_per_step / self.MICROSTEPS steps *= self.MICROSTEPS for i in range(steps): if reverse: self.currentstep -= 1 # go to next 'step' and wrap around self.currentstep += self.MICROSTEPS * 4 self.currentstep %= self.MICROSTEPS * 4 else: self.currentstep += 1 self.pwm_a = 0 self.pwm_b = 0 if (self.currentstep >= 0) and (self.currentstep < self.MICROSTEPS): self.pwm_a = self.MICROSTEP_CURVE[self.MICROSTEPS - self.currentstep] self.pwm_b = self.MICROSTEP_CURVE[self.currentstep] elif (self.currentstep >= self.MICROSTEPS) and (self.currentstep < self.MICROSTEPS * 2): self.pwm_a = self.MICROSTEP_CURVE[self.currentstep - self.MICROSTEPS] self.pwm_b = self.MICROSTEP_CURVE[self.MICROSTEPS * 2 - self.currentstep] elif (self.currentstep >= self.MICROSTEPS * 2) and (self.currentstep < self.MICROSTEPS * 3): self.pwm_a = self.MICROSTEP_CURVE[self.MICROSTEPS * 3 - self.currentstep] self.pwm_b = self.MICROSTEP_CURVE[self.currentstep - self.MICROSTEPS * 2] elif (self.currentstep >= self.MICROSTEPS * 3) and (self.currentstep < self.MICROSTEPS * 4): self.pwm_a = self.MICROSTEP_CURVE[self.currentstep - self.MICROSTEPS * 3] self.pwm_b = self.MICROSTEP_CURVE[self.MICROSTEPS * 4 - self.currentstep] self._step(microstep=True) time.sleep(s_per_s)
[docs] def align_step(self, reverse=False): """ Align the current step position with a full step :param reverse: run in reverse :type reverse: bool :return: None """ while (self.currentstep != 0) and (self.currentstep != self.MICROSTEPS): self.micro_step(reverse=reverse)
[docs]class AdafruitDCMotor(object): def __init__(self, controller, num): self.MC = controller self.motornum = num if self.motornum == 0: pwm = 8 in2 = 9 in1 = 10 elif self.motornum == 1: pwm = 13 in2 = 12 in1 = 11 elif self.motornum == 2: pwm = 2 in2 = 3 in1 = 4 elif self.motornum == 3: pwm = 7 in2 = 6 in1 = 5 else: raise ValueError('MotorHAT Motor must be between 0 and 3 inclusive') self.PWMpin = pwm self.IN1pin = in1 self.IN2pin = in2 def run(self, command): if not self.MC: return if command == AdafruitMotorHAT.FORWARD: self.MC.set_pin(self.IN2pin, 0) self.MC.set_pin(self.IN1pin, 1) if command == AdafruitMotorHAT.BACKWARD: self.MC.set_pin(self.IN1pin, 0) self.MC.set_pin(self.IN2pin, 1) if command == AdafruitMotorHAT.RELEASE: self.MC.set_pin(self.IN1pin, 0) self.MC.set_pin(self.IN2pin, 0) def setSpeed(self, speed): if speed < 0: speed = 0 if speed > 255: speed = 255 self.MC._pwm.setPWM(self.PWMpin, 0, speed * 16)
[docs]class AdafruitMotorHAT(object): FORWARD = 1 BACKWARD = 2 RELEASE = 3 def __init__(self, addr=0x60, freq=1600, i2c=None, i2c_bus=None): """ Initialize a Motor Hat :param addr: Motor Hat address :type addr: hex :param freq: pwm frequency :type freq: int :param i2c: the i2c device :param i2c_bus: the i2c bus to utilize """ self._frequency = freq self.motors = [AdafruitDCMotor(self, m) for m in range(4)] self.steppers = [AdafruitStepperMotor(self, 1), AdafruitStepperMotor(self, 2)] self._pwm = PWM(addr, debug=False, i2c=i2c, i2c_bus=i2c_bus) self._pwm.setPWMFreq(self._frequency)
[docs] def set_pin(self, pin, value): """ Set a pin output state :param pin: pin number :type pin: int :param value: pin state 0, or 1 :type value: int :return: None """ if pin not in range(16): raise ValueError('PWM pin must be between 0 and 15 inclusive') if value not in [0, 1]: raise ValueError('Pin value must be 0 or 1!') if value == 0: self._pwm.setPWM(pin, 0, 4096) if value == 1: self._pwm.setPWM(pin, 4096, 0)
[docs] def get_stepper(self, num): """ Get a stepper instance :param num: the stepper to get 1, or 2 :type num: int :return: :class:`AdafruitStepperMotor' """ if num not in [1, 2]: raise ValueError('MotorHAT Stepper must be between 1 and 2 inclusive') return self.steppers[num - 1]
[docs] def get_motor(self, num): """ Get a motor instance :param num: the stepper to get 1-4 :type num: int :return: :class:`AdafruitDCMotor' """ if num not in [1, 2, 3, 4]: raise ValueError('MotorHAT Motor must be between 1 and 4 inclusive') return self.motors[num - 1]