class StepperController: def __init__(self, num, logger=None): self._number = num self._ppr = 96 # pulses per revolution for stepper motor self.mh = Adafruit_MotorHAT() # create a default object, no changes to I2C address or frequency self.myStepper = self.mh.getStepper(self._ppr, self._number) #96 steps per revolution. Stepper port num. self.mypwm = Adafruit_PWM_Servo_Driver.PWM(0x60, debug=False) #I2C address 0x60 self.logger = logger or logging.getLogger(__name__) self.speed_pps = 20 #Speed in pulses per second self.steps = 0 self.step_type = 0 #SINGLE = 1 DOUBLE = 2 INTERLEAVE = 3 MICROSTEP = 4 self.motorq = Queue.Queue(5) self.motor_pos = 0 self.motor_pos_new = 0 self.motor_speed = 0 self.motor_half_step = True self.motor_running = False self.inv_dir = True #Constants from motorhat library num -= 1 self.FORWARD = 1 self.BACKWARD = 2 self.BRAKE = 3 self.RELEASE = 4 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: self.logger.error("MotorHAT Stepper must be between 1 and 2 inclusive") def write_motorq(self, command_tuple): #Tuple: command, speed, direction, motor.handle self.motorq.put(command_tuple) self.logger.debug("Putting " + str(command_tuple) + " : " + str(self.motorq.qsize()) + " items in queue") # Disable motors/ no force on poles! def turn_off_all(self ): self.mh.getMotor(1).run(Adafruit_MotorHAT.RELEASE) self.mh.getMotor(2).run(Adafruit_MotorHAT.RELEASE) self.mh.getMotor(3).run(Adafruit_MotorHAT.RELEASE) self.mh.getMotor(4).run(Adafruit_MotorHAT.RELEASE) def motor_set_speed(self, speed): self.motor_speed = speed self.speed_pps = float(speed)/60*self._ppr self.logger.info("Speed: {} RPM".format(speed)) if self.speed_pps > 95 and self.speed_pps < 193: self.mypwm.setPWMFreq( self.speed_pps/4) # Speed is 4 steps per frequency puls elif self.speed_pps < 96: self.mypwm.setPWMFreq(1600) self.myStepper.setSpeed(speed) else: self.logger.error("Speed {} rpm is to high or otherwise wrong".format(speed)) return def motor_set_step_type(self, half_step): self.motor_half_step = half_step if half_step: self.step_type = 3 else: self.step_type = 2 return def start_motor (self, position=None): if self.step_type != 2 and self.step_type != 3: self.motor_set_step_type(self.motor_half_step) if not position == None: self.motor_pos_new = position direction = self.FORWARD if self.motor_pos_new > self.motor_pos else self.BACKWARD #check direction (forward is True) if self.speed_pps > 95 and self.speed_pps < 193: #High speed stepping self.motorq.put(["fast", position, direction]) self.logger.debug("Putting " + str(["fast", position, direction]) + " : " + str(self.motorq.qsize()) + " items in queue") elif self.speed_pps < 96: self.motorq.put(["slow", position, direction]) self.logger.debug("speed_pps: {}".format(self.speed_pps)) self.logger.debug("Putting " + str(["slow", position, direction]) + " : " + str(self.motorq.qsize()) + " items in queue") else: self.logger.error("Speed {} rpm is to high or otherwise wrong".format(self.motor_speed)) return def release_motor (self): self.mh.setPin(self.AIN2, 0) self.mh.setPin(self.BIN1, 0) self.mh.setPin(self.AIN1, 0) self.mh.setPin(self.BIN2, 0) '''if self._number == 2: self.mh.getMotor(3).run(Adafruit_MotorHAT.RELEASE) self.mh.getMotor(4).run(Adafruit_MotorHAT.RELEASE) else : self.mh.getMotor(1).run(Adafruit_MotorHAT.RELEASE) self.mh.getMotor(2).run(Adafruit_MotorHAT.RELEASE)''' def stop_motor (self): self.motorq.put(["stop", 0]) self.myStepper.stop_stepper = True return def async_stop(self): self.myStepper.stop_stepper = True self.motorq.put(["exit", 0]) return def calc_pos(self, direction, start_time, run_type, step_count): if run_type == "slow": #if step_count != None: if self.motor_pos_new > self.motor_pos: self.motor_pos = self.motor_pos + step_count else: self.motor_pos = self.motor_pos - step_count elif run_type == "fast": if self.motor_pos_new > self.motor_pos: self.motor_pos =self.motor_pos + int((time.time() - start_time)*self.speed_pps * (2 if self.motor_half_step else 1)) else: self.motor_pos =self.motor_pos - int((time.time() - start_time)*self.speed_pps * (2 if self.motor_half_step else 1)) def async_motor (self): steps = 0 start_time = 0 run_time = 0 step_count = 0 running_type = "fast" while True: if not self.motorq.empty(): #Get items from Queue item = self.motorq.get() command = item[0] value = item[1] if len(item) == 3: motor_dir = item[2] elif len(item) > 3: motor_dir = item[2] motor_handle = item[3] self.logger.info("Getting " + str(item) + " : " + str(self.motorq.qsize()) + " items in queue") #direction = self.motor_pos_new > self.motor_pos #True = going out. steps = int(abs(self.motor_pos_new - self.motor_pos)) #Add steps if going out if command == "exit": self.turn_off_all() break elif command == "fast": running_type = command try: if self.motor_half_step: #t= steps/pulses per second run_time = (float(abs(steps))/float(self.speed_pps))/2 else: run_time = (float(abs(steps))/float(self.speed_pps)) if run_time > 0.1: if self.step_type == 2: if (motor_dir != self.inv_dir): self.mypwm.setPWM(self.AIN2, 0, 2048) self.mypwm.setPWM(self.BIN1, 1024, 3072) self.mypwm.setPWM(self.AIN1, 2048, 4095) self.mypwm.setPWM(self.BIN2, 3072, 512) else: self.mypwm.setPWM(self.AIN2, 3072, 512) self.mypwm.setPWM(self.BIN1, 2048, 4095) self.mypwm.setPWM(self.AIN1, 1024, 3072) self.mypwm.setPWM(self.BIN2, 0, 2048) elif self.step_type == 3: if (motor_dir != self.inv_dir): self.mypwm.setPWM(self.AIN2, 0, 1536) self.mypwm.setPWM(self.BIN1, 1024, 2560) self.mypwm.setPWM(self.AIN1, 2048, 3584) self.mypwm.setPWM(self.BIN2, 3072, 512) else: self.mypwm.setPWM(self.AIN2, 3072, 512) self.mypwm.setPWM(self.BIN1, 2048, 3584) self.mypwm.setPWM(self.AIN1, 1024, 2560) self.mypwm.setPWM(self.BIN2, 0, 1536) else: self.logger.error("Other step modes currently not supported") start_time = time.time() self.mypwm.setPWM(self.PWMA, 0, 4095) self.mypwm.setPWM(self.PWMB, 0, 4095) self.motor_running = True self.logger.info("Going to position {}".format(self.motor_pos_new)) else: pass except: self.logger.error("Start fast fault") elif command == "slow": running_type = command try: if self.step_type == 3: #t= steps/pulses per second run_time = (float(abs(steps))/float(self.speed_pps))/2 else: run_time = (float(abs(steps))/float(self.speed_pps)) start_time = time.time() self.motor_running = True self.logger.info("Going to position {}".format(self.motor_pos_new)) step_count = self.myStepper.step(steps, (motor_dir != self.inv_dir), self.step_type) self.release_motor() except: self.logger.exception("Start slow fault") elif command == "DCMotor": motor_handle.setSpeed(value) motor_handle.run(motor_dir) elif command == "stop" and self.motor_running: self.release_motor() self.motor_running = False self.calc_pos(motor_dir, start_time, running_type, step_count) run_time = 0 self.logger.info("Reached position {}, {}".format(self.motor_pos, motor_dir)) else: pass # Catch the time to stop the execution of fast moving motor. if self.motor_running and time.time() - start_time >= run_time: stop_time = time.time() self.logger.debug("Running time: {}, expected time: {}".format(stop_time - start_time, run_time)) run_time = 0 self.motor_running = False if running_type == "fast": self.mypwm.setPWM(self.PWMA, 0, 0) self.mypwm.setPWM(self.PWMB, 0, 0) self.calc_pos(motor_dir, start_time, running_type, step_count) elif running_type == "slow": self.calc_pos(motor_dir, start_time, running_type, step_count) self.logger.info("Reached position: {}, {}".format(self.motor_pos, motor_dir)) time.sleep(0.05) return