From 7d1a7a66f7d1a1e4196cff811926e2337df0f919 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 23 Jan 2015 21:03:22 +0000 Subject: [PATCH] init --- Adafruit_MotorHAT/Adafruit_MotorHAT.py | 252 +++++++++++++ .../Adafruit_PWM_Servo_Driver.py | 92 +++++ examples/DCTest.py | 62 ++++ examples/DualStepperTest.py | 67 ++++ examples/StackingTest.py | 71 ++++ examples/StepperTest.py | 38 ++ ez_setup.py | 332 ++++++++++++++++++ setup.py | 14 + 8 files changed, 928 insertions(+) create mode 100755 Adafruit_MotorHAT/Adafruit_MotorHAT.py create mode 100644 Adafruit_MotorHAT/Adafruit_PWM_Servo_Driver.py create mode 100644 examples/DCTest.py create mode 100644 examples/DualStepperTest.py create mode 100644 examples/StackingTest.py create mode 100644 examples/StepperTest.py create mode 100644 ez_setup.py create mode 100644 setup.py diff --git a/Adafruit_MotorHAT/Adafruit_MotorHAT.py b/Adafruit_MotorHAT/Adafruit_MotorHAT.py new file mode 100755 index 0000000..2fd603b --- /dev/null +++ b/Adafruit_MotorHAT/Adafruit_MotorHAT.py @@ -0,0 +1,252 @@ +#!/usr/bin/python + +from Adafruit_PWM_Servo_Driver import PWM +import time + +class Adafruit_StepperMotor: + 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): + self.MC = controller + self.revsteps = steps + self.motornum = num + self.sec_per_step = 0.1 + self.steppingcounter = 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 NameError('MotorHAT Stepper must be between 1 and 2 inclusive') + + def setSpeed(self, rpm): + self.sec_per_step = 60.0 / (self.revsteps * rpm) + self.steppingcounter = 0 + + def oneStep(self, dir, style): + pwm_a = pwm_b = 255 + + # first determine what sort of stepping procedure we're up to + if (style == Adafruit_MotorHAT.SINGLE): + if ((self.currentstep/(self.MICROSTEPS/2)) % 2): + # we're at an odd step, weird + if (dir == Adafruit_MotorHAT.FORWARD): + self.currentstep += self.MICROSTEPS/2 + else: + self.currentstep -= self.MICROSTEPS/2 + else: + # go to next even step + if (dir == Adafruit_MotorHAT.FORWARD): + self.currentstep += self.MICROSTEPS + else: + self.currentstep -= self.MICROSTEPS + if (style == Adafruit_MotorHAT.DOUBLE): + if not (self.currentstep/(self.MICROSTEPS/2) % 2): + # we're at an even step, weird + if (dir == Adafruit_MotorHAT.FORWARD): + self.currentstep += self.MICROSTEPS/2 + else: + self.currentstep -= self.MICROSTEPS/2 + else: + # go to next odd step + if (dir == Adafruit_MotorHAT.FORWARD): + self.currentstep += self.MICROSTEPS + else: + self.currentstep -= self.MICROSTEPS + if (style == Adafruit_MotorHAT.INTERLEAVE): + if (dir == Adafruit_MotorHAT.FORWARD): + self.currentstep += self.MICROSTEPS/2 + else: + self.currentstep -= self.MICROSTEPS/2 + + if (style == Adafruit_MotorHAT.MICROSTEP): + if (dir == Adafruit_MotorHAT.FORWARD): + self.currentstep += 1 + else: + self.currentstep -= 1 + + # go to next 'step' and wrap around + self.currentstep += self.MICROSTEPS * 4 + self.currentstep %= self.MICROSTEPS * 4 + + pwm_a = pwm_b = 0 + if (self.currentstep >= 0) and (self.currentstep < self.MICROSTEPS): + pwm_a = self.MICROSTEP_CURVE[self.MICROSTEPS - self.currentstep] + pwm_b = self.MICROSTEP_CURVE[self.currentstep] + elif (self.currentstep >= self.MICROSTEPS) and (self.currentstep < self.MICROSTEPS*2): + pwm_a = self.MICROSTEP_CURVE[self.currentstep - self.MICROSTEPS] + pwm_b = self.MICROSTEP_CURVE[self.MICROSTEPS*2 - self.currentstep] + elif (self.currentstep >= self.MICROSTEPS*2) and (self.currentstep < self.MICROSTEPS*3): + pwm_a = self.MICROSTEP_CURVE[self.MICROSTEPS*3 - self.currentstep] + pwm_b = self.MICROSTEP_CURVE[self.currentstep - self.MICROSTEPS*2] + elif (self.currentstep >= self.MICROSTEPS*3) and (self.currentstep < self.MICROSTEPS*4): + pwm_a = self.MICROSTEP_CURVE[self.currentstep - self.MICROSTEPS*3] + pwm_b = self.MICROSTEP_CURVE[self.MICROSTEPS*4 - self.currentstep] + + + # 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, pwm_a*16) + self.MC._pwm.setPWM(self.PWMB, 0, pwm_b*16) + + # set up coil energizing! + coils = [0, 0, 0, 0] + + if (style == Adafruit_MotorHAT.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)] + + #print "coils state = " + str(coils) + self.MC.setPin(self.AIN2, coils[0]) + self.MC.setPin(self.BIN1, coils[1]) + self.MC.setPin(self.AIN1, coils[2]) + self.MC.setPin(self.BIN2, coils[3]) + + return self.currentstep + + def step(self, steps, direction, stepstyle): + s_per_s = self.sec_per_step + lateststep = 0 + + if (stepstyle == Adafruit_MotorHAT.INTERLEAVE): + s_per_s = s_per_s / 2.0 + if (stepstyle == Adafruit_MotorHAT.MICROSTEP): + s_per_s /= self.MICROSTEPS + steps *= self.MICROSTEPS + + print s_per_s, " sec per step" + + for s in range(steps): + lateststep = self.oneStep(direction, stepstyle) + time.sleep(s_per_s) + + if (stepstyle == Adafruit_MotorHAT.MICROSTEP): + # this is an edge case, if we are in between full steps, lets just keep going + # so we end on a full step + while (lateststep != 0) and (lateststep != self.MICROSTEPS): + lateststep = self.oneStep(dir, stepstyle) + time.sleep(s_per_s) + +class Adafruit_DCMotor: + def __init__(self, controller, num): + self.MC = controller + self.motornum = num + pwm = in1 = in2 = 0 + + if (num == 0): + pwm = 8 + in2 = 9 + in1 = 10 + elif (num == 1): + pwm = 13 + in2 = 12 + in1 = 11 + elif (num == 2): + pwm = 2 + in2 = 3 + in1 = 4 + elif (num == 3): + pwm = 7 + in2 = 6 + in1 = 5 + else: + raise NameError('MotorHAT Motor must be between 1 and 4 inclusive') + self.PWMpin = pwm + self.IN1pin = in1 + self.IN2pin = in2 + + def run(self, command): + if not self.MC: + return + if (command == Adafruit_MotorHAT.FORWARD): + self.MC.setPin(self.IN2pin, 0) + self.MC.setPin(self.IN1pin, 1) + if (command == Adafruit_MotorHAT.BACKWARD): + self.MC.setPin(self.IN1pin, 0) + self.MC.setPin(self.IN2pin, 1) + if (command == Adafruit_MotorHAT.RELEASE): + self.MC.setPin(self.IN1pin, 0) + self.MC.setPin(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) + +class Adafruit_MotorHAT: + FORWARD = 1 + BACKWARD = 2 + BRAKE = 3 + RELEASE = 4 + + SINGLE = 1 + DOUBLE = 2 + INTERLEAVE = 3 + MICROSTEP = 4 + + def __init__(self, addr = 0x60, freq = 1600): + self._i2caddr = addr # default addr on HAT + self._frequency = freq # default @1600Hz PWM freq + self.motors = [ Adafruit_DCMotor(self, m) for m in range(4) ] + self.steppers = [ Adafruit_StepperMotor(self, 1), Adafruit_StepperMotor(self, 2) ] + self._pwm = PWM(addr, debug=False) + self._pwm.setPWMFreq(self._frequency) + + def setPin(self, pin, value): + if (pin < 0) or (pin > 15): + raise NameError('PWM pin must be between 0 and 15 inclusive') + if (value != 0) and (value != 1): + raise NameError('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) + + def getStepper(self, steps, num): + if (num < 1) or (num > 2): + raise NameError('MotorHAT Stepper must be between 1 and 2 inclusive') + return self.steppers[num-1] + + def getMotor(self, num): + if (num < 1) or (num > 4): + raise NameError('MotorHAT Motor must be between 1 and 4 inclusive') + return self.motors[num-1] diff --git a/Adafruit_MotorHAT/Adafruit_PWM_Servo_Driver.py b/Adafruit_MotorHAT/Adafruit_PWM_Servo_Driver.py new file mode 100644 index 0000000..35c993c --- /dev/null +++ b/Adafruit_MotorHAT/Adafruit_PWM_Servo_Driver.py @@ -0,0 +1,92 @@ +#!/usr/bin/python + +import time +import math +from Adafruit_I2C import Adafruit_I2C + +# ============================================================================ +# Adafruit PCA9685 16-Channel PWM Servo Driver +# ============================================================================ + +class PWM : + # Registers/etc. + __MODE1 = 0x00 + __MODE2 = 0x01 + __SUBADR1 = 0x02 + __SUBADR2 = 0x03 + __SUBADR3 = 0x04 + __PRESCALE = 0xFE + __LED0_ON_L = 0x06 + __LED0_ON_H = 0x07 + __LED0_OFF_L = 0x08 + __LED0_OFF_H = 0x09 + __ALL_LED_ON_L = 0xFA + __ALL_LED_ON_H = 0xFB + __ALL_LED_OFF_L = 0xFC + __ALL_LED_OFF_H = 0xFD + + # Bits + __RESTART = 0x80 + __SLEEP = 0x10 + __ALLCALL = 0x01 + __INVRT = 0x10 + __OUTDRV = 0x04 + + general_call_i2c = Adafruit_I2C(0x00) + + @classmethod + def softwareReset(cls): + "Sends a software reset (SWRST) command to all the servo drivers on the bus" + cls.general_call_i2c.writeRaw8(0x06) # SWRST + + def __init__(self, address=0x40, debug=False): + self.i2c = Adafruit_I2C(address) + self.i2c.debug = debug + self.address = address + self.debug = debug + if (self.debug): + print "Reseting PCA9685 MODE1 (without SLEEP) and MODE2" + self.setAllPWM(0, 0) + self.i2c.write8(self.__MODE2, self.__OUTDRV) + self.i2c.write8(self.__MODE1, self.__ALLCALL) + time.sleep(0.005) # wait for oscillator + + mode1 = self.i2c.readU8(self.__MODE1) + mode1 = mode1 & ~self.__SLEEP # wake up (reset sleep) + self.i2c.write8(self.__MODE1, mode1) + time.sleep(0.005) # wait for oscillator + + def setPWMFreq(self, freq): + "Sets the PWM frequency" + prescaleval = 25000000.0 # 25MHz + prescaleval /= 4096.0 # 12-bit + prescaleval /= float(freq) + prescaleval -= 1.0 + if (self.debug): + print "Setting PWM frequency to %d Hz" % freq + print "Estimated pre-scale: %d" % prescaleval + prescale = math.floor(prescaleval + 0.5) + if (self.debug): + print "Final pre-scale: %d" % prescale + + oldmode = self.i2c.readU8(self.__MODE1); + newmode = (oldmode & 0x7F) | 0x10 # sleep + self.i2c.write8(self.__MODE1, newmode) # go to sleep + self.i2c.write8(self.__PRESCALE, int(math.floor(prescale))) + self.i2c.write8(self.__MODE1, oldmode) + time.sleep(0.005) + self.i2c.write8(self.__MODE1, oldmode | 0x80) + + def setPWM(self, channel, on, off): + "Sets a single PWM channel" + self.i2c.write8(self.__LED0_ON_L+4*channel, on & 0xFF) + self.i2c.write8(self.__LED0_ON_H+4*channel, on >> 8) + self.i2c.write8(self.__LED0_OFF_L+4*channel, off & 0xFF) + self.i2c.write8(self.__LED0_OFF_H+4*channel, off >> 8) + + def setAllPWM(self, on, off): + "Sets a all PWM channels" + self.i2c.write8(self.__ALL_LED_ON_L, on & 0xFF) + self.i2c.write8(self.__ALL_LED_ON_H, on >> 8) + self.i2c.write8(self.__ALL_LED_OFF_L, off & 0xFF) + self.i2c.write8(self.__ALL_LED_OFF_H, off >> 8) diff --git a/examples/DCTest.py b/examples/DCTest.py new file mode 100644 index 0000000..c840a84 --- /dev/null +++ b/examples/DCTest.py @@ -0,0 +1,62 @@ +#!/usr/bin/python +#import Adafruit_MotorHAT, Adafruit_DCMotor, Adafruit_Stepper +from Adafruit_MotorHAT import Adafruit_MotorHAT, Adafruit_DCMotor, Adafruit_StepperMotor + +import time +import atexit + +# create a default object, no changes to I2C address or frequency +mh = Adafruit_MotorHAT(addr=0x61) + +# recommended for auto-disabling motors on shutdown! +def turnOffMotors(): + mh.getMotor(1).run(Adafruit_MotorHAT.RELEASE) + mh.getMotor(2).run(Adafruit_MotorHAT.RELEASE) + mh.getMotor(3).run(Adafruit_MotorHAT.RELEASE) + mh.getMotor(4).run(Adafruit_MotorHAT.RELEASE) + +atexit.register(turnOffMotors) + +################################# DC motor test! +myMotor = mh.getMotor(3) + +# set the speed to start, from 0 (off) to 255 (max speed) +myMotor.setSpeed(150) +myMotor.run(Adafruit_MotorHAT.FORWARD); +# turn on motor +myMotor.run(Adafruit_MotorHAT.RELEASE); + + +while (True): + print "Forward! " + myMotor.run(Adafruit_MotorHAT.FORWARD) + + print "\tSpeed up..." + for i in range(255): + myMotor.setSpeed(i) + time.sleep(0.01) + + print "\tSlow down..." + for i in reversed(range(255)): + myMotor.setSpeed(i) + time.sleep(0.01) + + print "Backward! " + myMotor.run(Adafruit_MotorHAT.BACKWARD) + + print "\tSpeed up..." + for i in range(255): + myMotor.setSpeed(i) + time.sleep(0.01) + + print "\tSlow down..." + for i in reversed(range(255)): + myMotor.setSpeed(i) + time.sleep(0.01) + + print "Release" + myMotor.run(Adafruit_MotorHAT.RELEASE) + time.sleep(1.0) + + + diff --git a/examples/DualStepperTest.py b/examples/DualStepperTest.py new file mode 100644 index 0000000..8a8ea08 --- /dev/null +++ b/examples/DualStepperTest.py @@ -0,0 +1,67 @@ +#!/usr/bin/python +from Adafruit_MotorHAT import Adafruit_MotorHAT, Adafruit_DCMotor, Adafruit_StepperMotor +import time +import atexit +import threading +import random + +# create a default object, no changes to I2C address or frequency +mh = Adafruit_MotorHAT() + +# create empty threads (these will hold the stepper 1 and 2 threads) +st1 = threading.Thread() +st2 = threading.Thread() + + +# recommended for auto-disabling motors on shutdown! +def turnOffMotors(): + mh.getMotor(1).run(Adafruit_MotorHAT.RELEASE) + mh.getMotor(2).run(Adafruit_MotorHAT.RELEASE) + mh.getMotor(3).run(Adafruit_MotorHAT.RELEASE) + mh.getMotor(4).run(Adafruit_MotorHAT.RELEASE) + +atexit.register(turnOffMotors) + +myStepper1 = mh.getStepper(200, 1) # 200 steps/rev, motor port #1 +myStepper2 = mh.getStepper(200, 2) # 200 steps/rev, motor port #1 +myStepper1.setSpeed(60) # 30 RPM +myStepper2.setSpeed(60) # 30 RPM + + +stepstyles = [Adafruit_MotorHAT.SINGLE, Adafruit_MotorHAT.DOUBLE, Adafruit_MotorHAT.INTERLEAVE, Adafruit_MotorHAT.MICROSTEP] + +def stepper_worker(stepper, numsteps, direction, style): + #print("Steppin!") + stepper.step(numsteps, direction, style) + #print("Done") + +while (True): + if not st1.isAlive(): + randomdir = random.randint(0, 1) + print("Stepper 1"), + if (randomdir == 0): + dir = Adafruit_MotorHAT.FORWARD + print("forward"), + else: + dir = Adafruit_MotorHAT.BACKWARD + print("backward"), + randomsteps = random.randint(10,50) + print("%d steps" % randomsteps) + st1 = threading.Thread(target=stepper_worker, args=(myStepper1, randomsteps, dir, stepstyles[random.randint(0,3)],)) + st1.start() + + if not st2.isAlive(): + print("Stepper 2"), + randomdir = random.randint(0, 1) + if (randomdir == 0): + dir = Adafruit_MotorHAT.FORWARD + print("forward"), + else: + dir = Adafruit_MotorHAT.BACKWARD + print("backward"), + + randomsteps = random.randint(10,50) + print("%d steps" % randomsteps) + + st2 = threading.Thread(target=stepper_worker, args=(myStepper2, randomsteps, dir, stepstyles[random.randint(0,3)],)) + st2.start() diff --git a/examples/StackingTest.py b/examples/StackingTest.py new file mode 100644 index 0000000..9a0d016 --- /dev/null +++ b/examples/StackingTest.py @@ -0,0 +1,71 @@ +#!/usr/bin/python +from Adafruit_MotorHAT import Adafruit_MotorHAT, Adafruit_DCMotor, Adafruit_StepperMotor +import time +import atexit +import threading +import random + +# bottom hat is default address 0x60 +bottomhat = Adafruit_MotorHAT(addr=0x60) +# top hat has A0 jumper closed, so its address 0x61 +tophat = Adafruit_MotorHAT(addr=0x61) + +# create empty threads (these will hold the stepper 1, 2 & 3 threads) +stepperThreads = [threading.Thread(), threading.Thread(), threading.Thread()] + +# recommended for auto-disabling motors on shutdown! +def turnOffMotors(): + tophat.getMotor(1).run(Adafruit_MotorHAT.RELEASE) + tophat.getMotor(2).run(Adafruit_MotorHAT.RELEASE) + tophat.getMotor(3).run(Adafruit_MotorHAT.RELEASE) + tophat.getMotor(4).run(Adafruit_MotorHAT.RELEASE) + bottomhat.getMotor(1).run(Adafruit_MotorHAT.RELEASE) + bottomhat.getMotor(2).run(Adafruit_MotorHAT.RELEASE) + bottomhat.getMotor(3).run(Adafruit_MotorHAT.RELEASE) + bottomhat.getMotor(4).run(Adafruit_MotorHAT.RELEASE) + +atexit.register(turnOffMotors) + +myStepper1 = bottomhat.getStepper(200, 1) # 200 steps/rev, motor port #1 +myStepper2 = bottomhat.getStepper(200, 2) # 200 steps/rev, motor port #2 +myStepper3 = tophat.getStepper(200, 1) # 200 steps/rev, motor port #1 + +myStepper1.setSpeed(60) # 60 RPM +myStepper2.setSpeed(30) # 30 RPM +myStepper3.setSpeed(15) # 15 RPM + +# get a DC motor! +myMotor = tophat.getMotor(3) +# set the speed to start, from 0 (off) to 255 (max speed) +myMotor.setSpeed(150) +# turn on motor +myMotor.run(Adafruit_MotorHAT.FORWARD); + + +stepstyles = [Adafruit_MotorHAT.SINGLE, Adafruit_MotorHAT.DOUBLE, Adafruit_MotorHAT.INTERLEAVE] +steppers = [myStepper1, myStepper2, myStepper3] + +def stepper_worker(stepper, numsteps, direction, style): + #print("Steppin!") + stepper.step(numsteps, direction, style) + #print("Done") + +while (True): + for i in range(3): + if not stepperThreads[i].isAlive(): + randomdir = random.randint(0, 1) + print("Stepper %d" % i), + if (randomdir == 0): + dir = Adafruit_MotorHAT.FORWARD + print("forward"), + else: + dir = Adafruit_MotorHAT.BACKWARD + print("backward"), + randomsteps = random.randint(10,50) + print("%d steps" % randomsteps) + stepperThreads[i] = threading.Thread(target=stepper_worker, args=(steppers[i], randomsteps, dir, stepstyles[random.randint(0,len(stepstyles)-1)],)) + stepperThreads[i].start() + + # also, lets switch around the DC motor! + myMotor.setSpeed(random.randint(0,255)) # random speed + #myMotor.run(random.randint(0,1)) # random forward/back diff --git a/examples/StepperTest.py b/examples/StepperTest.py new file mode 100644 index 0000000..d10c81e --- /dev/null +++ b/examples/StepperTest.py @@ -0,0 +1,38 @@ +#!/usr/bin/python +#import Adafruit_MotorHAT, Adafruit_DCMotor, Adafruit_Stepper +from Adafruit_MotorHAT import Adafruit_MotorHAT, Adafruit_DCMotor, Adafruit_StepperMotor + +import time +import atexit + +# create a default object, no changes to I2C address or frequency +mh = Adafruit_MotorHAT() + +# recommended for auto-disabling motors on shutdown! +def turnOffMotors(): + mh.getMotor(1).run(Adafruit_MotorHAT.RELEASE) + mh.getMotor(2).run(Adafruit_MotorHAT.RELEASE) + mh.getMotor(3).run(Adafruit_MotorHAT.RELEASE) + mh.getMotor(4).run(Adafruit_MotorHAT.RELEASE) + +atexit.register(turnOffMotors) + +myStepper = mh.getStepper(200, 2) # 200 steps/rev, motor port #1 +myStepper.setSpeed(30) # 30 RPM + +while (True): + print("Single coil steps") + myStepper.step(100, Adafruit_MotorHAT.FORWARD, Adafruit_MotorHAT.SINGLE) + myStepper.step(100, Adafruit_MotorHAT.BACKWARD, Adafruit_MotorHAT.SINGLE) + + print("Double coil steps") + myStepper.step(100, Adafruit_MotorHAT.FORWARD, Adafruit_MotorHAT.DOUBLE) + myStepper.step(100, Adafruit_MotorHAT.BACKWARD, Adafruit_MotorHAT.DOUBLE) + + print("Interleaved coil steps") + myStepper.step(100, Adafruit_MotorHAT.FORWARD, Adafruit_MotorHAT.INTERLEAVE) + myStepper.step(100, Adafruit_MotorHAT.BACKWARD, Adafruit_MotorHAT.INTERLEAVE) + + print("Microsteps") + myStepper.step(100, Adafruit_MotorHAT.FORWARD, Adafruit_MotorHAT.MICROSTEP) + myStepper.step(100, Adafruit_MotorHAT.BACKWARD, Adafruit_MotorHAT.MICROSTEP) diff --git a/ez_setup.py b/ez_setup.py new file mode 100644 index 0000000..23ea9a2 --- /dev/null +++ b/ez_setup.py @@ -0,0 +1,332 @@ +#!/usr/bin/env python +"""Bootstrap setuptools installation + +To use setuptools in your package's setup.py, include this +file in the same directory and add this to the top of your setup.py:: + + from ez_setup import use_setuptools + use_setuptools() + +To require a specific version of setuptools, set a download +mirror, or use an alternate download directory, simply supply +the appropriate options to ``use_setuptools()``. + +This file can also be run as a script to install or upgrade setuptools. +""" +import os +import shutil +import sys +import tempfile +import zipfile +import optparse +import subprocess +import platform +import textwrap +import contextlib + +from distutils import log + +try: + from site import USER_SITE +except ImportError: + USER_SITE = None + +DEFAULT_VERSION = "3.5.1" +DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/" + +def _python_cmd(*args): + """ + Return True if the command succeeded. + """ + args = (sys.executable,) + args + return subprocess.call(args) == 0 + + +def _install(archive_filename, install_args=()): + with archive_context(archive_filename): + # installing + log.warn('Installing Setuptools') + if not _python_cmd('setup.py', 'install', *install_args): + log.warn('Something went wrong during the installation.') + log.warn('See the error message above.') + # exitcode will be 2 + return 2 + + +def _build_egg(egg, archive_filename, to_dir): + with archive_context(archive_filename): + # building an egg + log.warn('Building a Setuptools egg in %s', to_dir) + _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) + # returning the result + log.warn(egg) + if not os.path.exists(egg): + raise IOError('Could not build the egg.') + + +def get_zip_class(): + """ + Supplement ZipFile class to support context manager for Python 2.6 + """ + class ContextualZipFile(zipfile.ZipFile): + def __enter__(self): + return self + def __exit__(self, type, value, traceback): + self.close + return zipfile.ZipFile if hasattr(zipfile.ZipFile, '__exit__') else \ + ContextualZipFile + + +@contextlib.contextmanager +def archive_context(filename): + # extracting the archive + tmpdir = tempfile.mkdtemp() + log.warn('Extracting in %s', tmpdir) + old_wd = os.getcwd() + try: + os.chdir(tmpdir) + with get_zip_class()(filename) as archive: + archive.extractall() + + # going in the directory + subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) + os.chdir(subdir) + log.warn('Now working in %s', subdir) + yield + + finally: + os.chdir(old_wd) + shutil.rmtree(tmpdir) + + +def _do_download(version, download_base, to_dir, download_delay): + egg = os.path.join(to_dir, 'setuptools-%s-py%d.%d.egg' + % (version, sys.version_info[0], sys.version_info[1])) + if not os.path.exists(egg): + archive = download_setuptools(version, download_base, + to_dir, download_delay) + _build_egg(egg, archive, to_dir) + sys.path.insert(0, egg) + + # Remove previously-imported pkg_resources if present (see + # https://bitbucket.org/pypa/setuptools/pull-request/7/ for details). + if 'pkg_resources' in sys.modules: + del sys.modules['pkg_resources'] + + import setuptools + setuptools.bootstrap_install_from = egg + + +def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, + to_dir=os.curdir, download_delay=15): + to_dir = os.path.abspath(to_dir) + rep_modules = 'pkg_resources', 'setuptools' + imported = set(sys.modules).intersection(rep_modules) + try: + import pkg_resources + except ImportError: + return _do_download(version, download_base, to_dir, download_delay) + try: + pkg_resources.require("setuptools>=" + version) + return + except pkg_resources.DistributionNotFound: + return _do_download(version, download_base, to_dir, download_delay) + except pkg_resources.VersionConflict as VC_err: + if imported: + msg = textwrap.dedent(""" + The required version of setuptools (>={version}) is not available, + and can't be installed while this script is running. Please + install a more recent version first, using + 'easy_install -U setuptools'. + + (Currently using {VC_err.args[0]!r}) + """).format(VC_err=VC_err, version=version) + sys.stderr.write(msg) + sys.exit(2) + + # otherwise, reload ok + del pkg_resources, sys.modules['pkg_resources'] + return _do_download(version, download_base, to_dir, download_delay) + +def _clean_check(cmd, target): + """ + Run the command to download target. If the command fails, clean up before + re-raising the error. + """ + try: + subprocess.check_call(cmd) + except subprocess.CalledProcessError: + if os.access(target, os.F_OK): + os.unlink(target) + raise + +def download_file_powershell(url, target): + """ + Download the file at url to target using Powershell (which will validate + trust). Raise an exception if the command cannot complete. + """ + target = os.path.abspath(target) + cmd = [ + 'powershell', + '-Command', + "(new-object System.Net.WebClient).DownloadFile(%(url)r, %(target)r)" % vars(), + ] + _clean_check(cmd, target) + +def has_powershell(): + if platform.system() != 'Windows': + return False + cmd = ['powershell', '-Command', 'echo test'] + devnull = open(os.path.devnull, 'wb') + try: + try: + subprocess.check_call(cmd, stdout=devnull, stderr=devnull) + except Exception: + return False + finally: + devnull.close() + return True + +download_file_powershell.viable = has_powershell + +def download_file_curl(url, target): + cmd = ['curl', url, '--silent', '--output', target] + _clean_check(cmd, target) + +def has_curl(): + cmd = ['curl', '--version'] + devnull = open(os.path.devnull, 'wb') + try: + try: + subprocess.check_call(cmd, stdout=devnull, stderr=devnull) + except Exception: + return False + finally: + devnull.close() + return True + +download_file_curl.viable = has_curl + +def download_file_wget(url, target): + cmd = ['wget', url, '--quiet', '--output-document', target] + _clean_check(cmd, target) + +def has_wget(): + cmd = ['wget', '--version'] + devnull = open(os.path.devnull, 'wb') + try: + try: + subprocess.check_call(cmd, stdout=devnull, stderr=devnull) + except Exception: + return False + finally: + devnull.close() + return True + +download_file_wget.viable = has_wget + +def download_file_insecure(url, target): + """ + Use Python to download the file, even though it cannot authenticate the + connection. + """ + try: + from urllib.request import urlopen + except ImportError: + from urllib2 import urlopen + src = dst = None + try: + src = urlopen(url) + # Read/write all in one block, so we don't create a corrupt file + # if the download is interrupted. + data = src.read() + dst = open(target, "wb") + dst.write(data) + finally: + if src: + src.close() + if dst: + dst.close() + +download_file_insecure.viable = lambda: True + +def get_best_downloader(): + downloaders = [ + download_file_powershell, + download_file_curl, + download_file_wget, + download_file_insecure, + ] + + for dl in downloaders: + if dl.viable(): + return dl + +def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, + to_dir=os.curdir, delay=15, downloader_factory=get_best_downloader): + """ + Download setuptools from a specified location and return its filename + + `version` should be a valid setuptools version number that is available + as an egg for download under the `download_base` URL (which should end + with a '/'). `to_dir` is the directory where the egg will be downloaded. + `delay` is the number of seconds to pause before an actual download + attempt. + + ``downloader_factory`` should be a function taking no arguments and + returning a function for downloading a URL to a target. + """ + # making sure we use the absolute path + to_dir = os.path.abspath(to_dir) + zip_name = "setuptools-%s.zip" % version + url = download_base + zip_name + saveto = os.path.join(to_dir, zip_name) + if not os.path.exists(saveto): # Avoid repeated downloads + log.warn("Downloading %s", url) + downloader = downloader_factory() + downloader(url, saveto) + return os.path.realpath(saveto) + +def _build_install_args(options): + """ + Build the arguments to 'python setup.py install' on the setuptools package + """ + return ['--user'] if options.user_install else [] + +def _parse_args(): + """ + Parse the command line for options + """ + parser = optparse.OptionParser() + parser.add_option( + '--user', dest='user_install', action='store_true', default=False, + help='install in user site package (requires Python 2.6 or later)') + parser.add_option( + '--download-base', dest='download_base', metavar="URL", + default=DEFAULT_URL, + help='alternative URL from where to download the setuptools package') + parser.add_option( + '--insecure', dest='downloader_factory', action='store_const', + const=lambda: download_file_insecure, default=get_best_downloader, + help='Use internal, non-validating downloader' + ) + parser.add_option( + '--version', help="Specify which version to download", + default=DEFAULT_VERSION, + ) + options, args = parser.parse_args() + # positional arguments are ignored + return options + +def main(): + """Install or upgrade setuptools and EasyInstall""" + options = _parse_args() + archive = download_setuptools( + version=options.version, + download_base=options.download_base, + downloader_factory=options.downloader_factory, + ) + return _install(archive, _build_install_args(options)) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..6aea3c3 --- /dev/null +++ b/setup.py @@ -0,0 +1,14 @@ +from ez_setup import use_setuptools +use_setuptools() +from setuptools import setup, find_packages + +setup(name = 'Adafruit_MotorHAT', + version = '1.0.0', + author = 'Limor Fried', + author_email = 'support@adafruit.com', + description = 'Library for Adafruit Motor HAT', + license = 'MIT', + url = 'https://github.com/adafruit/Adafruit_Python_MotorHAT/', + dependency_links = ['https://github.com/adafruit/Adafruit_Python_GPIO/tarball/master#egg=Adafruit-GPIO-0.7'], + install_requires = ['Adafruit-GPIO>=0.7'], + packages = find_packages())