gpiozero icon indicating copy to clipboard operation
gpiozero copied to clipboard

Add Stepper Motor

Open bennuttall opened this issue 10 years ago • 5 comments

Here's someone using OutputDevice: https://github.com/topshed/PiClubSushi/blob/master/17-Stepper.pdf

Edit: moved to https://github.com/topshed/PiClubSushi/blob/master/17-StepperBETA.pdf

bennuttall avatar Jan 10 '16 15:01 bennuttall

More info here: http://www.winkleink.com/2013/04/raspberry-pi-unipolar-stepper-motors.html

bennuttall avatar Jun 12 '16 22:06 bennuttall

Does anyone have strong opinions on how the API for the stepper motor should look?

Full disclosure... Most of my knowledge of stepper motors comes from this video: https://www.youtube.com/watch?v=Dc16mKFA7Fo and the little bit I've picked up while using one. I plan to contribute a class for StepperMotor if this is still wanted, but thought it would be good to solicit early feedback.

I propose the following methods, each of which would accept a number for the total rotations, eg .75, 1, 1.12, -2, etc, where negative numbers would rotate the stepper motor in a "backwards" direction (if possible, which I assume it is):

  • single_step
  • full_step
  • half_step

The constructor would accept the following arguments:

  • pins: list of pin numbers to use
  • steps_per_rev: the number of steps required for a full revolution without gearing
  • gear_ratio: an integer representing the denominator of the ratio (1/64 gearing = 64)

Is there anything else anyone would like to see as part of this? Is there anything that should be different from the proposal?

lreading avatar Oct 03 '17 03:10 lreading

Hello,

I know it's an old topic but I use a stepper motor NEMA 17 JK42HM34-1334 with a L298N controller. I started a class to control forward / backward as follow, this is following the method documented in the PDF :

import time
import sys
import threading
from gpiozero import OutputDevice
from pprint import pprint


class stepMotors:
  def __init__(self, pins=[6,13,19,26]):
    self.motorBase = []
    self.pins = pins
    for pin in self.pins:
      self.motorBase.append(OutputDevice(pin))

    self.seq = [
        [1,0,1,0],
        [1,0,0,1],
        [0,1,0,1],
        [0,1,1,0]
    ]
    self.state = False

  def cleanup(self):
    if self.thread.is_alive():
      self.state = False
      self.thread.join()

    for GpioOutputDevice in self.motorBase:
      GpioOutputDevice.off()

    return True


  def forward(self):
    if self.state:
      self.cleanup()

    self.direction = 1
    self.stepCounter = 0
    self.state = True
    self.thread = threading.Thread(target=self.run, args=())
    self.thread.daemon = True
    self.thread.start()


  def backward(self):
    if self.state:
      self.cleanup()

    self.direction = -1
    self.stepCounter = 0
    self.state = True
    self.thread = threading.Thread(target=self.run, args=())
    self.thread.daemon = True
    self.thread.start()

  def run(self):
    waitTime = 0.001
    stepCount=len(self.seq)
    try:
      while self.state:
        for pin in range(0,4):
          xPin=self.motorBase[pin]
          if self.seq[self.stepCounter][pin]!=0:
            xPin.on()
          else:
            xPin.off()
        time.sleep(waitTime)
        self.stepCounter += self.direction
        if (self.stepCounter >= stepCount):
          self.stepCounter = 0
        if (self.stepCounter < 0):
          self.stepCounter = stepCount+self.direction
    except:
      raise()

Can be use like :

move = stepMotors()
move.forward()
time.sleep(4)
move.backward()
time.sleep(2)
move.forward()
time.sleep(3)
move.backward()
time.sleep(4)

Do you see a way to improve it ?

thanks

fas3r avatar Jan 15 '19 18:01 fas3r

I needed this on a project so I wrote one myself (https://github.com/gfabia/raspberrypi-gpiozero-stepmotor). I simply adapted Arduino's built-in Stepper library to Python.

Here's my code (gpiostepper.py):

from gpiozero import OutputDevice
from time import sleep

class Stepper:

    CW = -1
    CCW = 1

    """Constructor"""
    def __init__(self, motor_pins, number_of_steps = 32, step_sequence = [[1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1]]):
        self.motor_pins = [OutputDevice(pin) for pin in motor_pins]  # Control pins
        self.pin_count = len(motor_pins)                             # Number of control pins 
        self.step_sequence = step_sequence                           # Sequence of control signals   
        self.step_number = 0                                         # Which step the motor is on
        self.number_of_steps = number_of_steps                       # Total number of steps per internal motor revolution
        self.direction = self.CW                                     # Rotation direction
        self.step_delay = 60 / self.number_of_steps / 240            # Rotation delay (240rpm == 3.90625ms delay)
        
    """Sets speed in revolutions per minute"""
    def set_speed(self, what_speed):
        self.step_delay = 60 / self.number_of_steps / what_speed     # Step delay in seconds
        print("Step Delay: {}ms".format(self.step_delay * 1000)) 

    """Moves the motor steps_to_move steps.  If the number is negative, the motor moves in the reverse direction."""
    def step(self, steps_to_move):

        # Determine how many steps to left to take
        steps_left = int(abs(steps_to_move))

        # Determine direction
        self.direction = self.CW if steps_to_move > 0 else self.CCW

        # Decrement the number of steps, moving one step each time
        while steps_left > 0:
            if self.direction == self.CCW:
                self.step_number = (self.step_number + 1) % self.number_of_steps
            else:
                self.step_number = (self.step_number - 1) % self.number_of_steps
                
            steps_left -= 1
            self.step_motor()

    """Moves the motor forward or backwards"""
    def step_motor(self):

        # Select the correct control signal sequence
        this_step = self.step_number % len(self.step_sequence)
        seq = self.step_sequence[this_step]

        # Set pin state accordingly
        for pin in range(self.pin_count):
            if seq[pin] == 1:
                self.motor_pins[pin].on()
            else:
                self.motor_pins[pin].off()

        sleep(self.step_delay)

    """Rotates the motor clockwise indefinitely"""
    def forward(self):
        while True:
            self.step_number = (self.step_number - 1) % self.number_of_steps
            self.step_motor()

    """Rotates the motor counter-clockwise indefinitely"""
    def backward(self):
        while True:
            self.step_number = (self.step_number + 1) % self.number_of_steps
            self.step_motor()

In case you want precise number of turns, you can use it as follows:

import sys
from time import sleep
from gpiostepper import Stepper

if __name__ == "__main__":
    
    if len(sys.argv) > 1:
        speed = int(sys.argv[1])
    else:
        speed = 100
    
    print("Speed: {} rpm".format(speed))

    number_of_steps = 32

    step_motor1 = Stepper(motor_pins=[17, 18, 22, 23], number_of_steps = number_of_steps)
    step_motor1.set_speed(speed)
    
    amount_of_gear_reduction = 64
    number_of_steps_per_revolution_geared_output = number_of_steps * amount_of_gear_reduction
    
    # Do a full CW rotation
    print("One Full Clockwise Rotation")
    step_motor1.step(number_of_steps_per_revolution_geared_output)
    
    sleep(1)
    
    # Do a half CCW rotation
    print("Half Counter-clockwise Rotation Slowly")
    step_motor1.set_speed(speed/3)
    step_motor1.step(-number_of_steps_per_revolution_geared_output/2)

You can also set the step sequence like so:

import sys
from time import sleep
from gpiostepper import Stepper

if __name__ == "__main__":
    
    if len(sys.argv) > 1:
        speed = int(sys.argv[1])
    else:
        speed = 100
    
    print("Speed: {} rpm".format(speed))

    number_of_steps = 32
    
    step_motor2 = Stepper(
        motor_pins=[17, 22, 18, 23], 
        number_of_steps = number_of_steps,
        step_sequence = [[1,0,1,0], [0,1,1,0], [0,1,0,1], [1,0,0,1]]
        )
    step_motor2.set_speed(speed)
    
    # Rotate CW indefinitely
    print("Clockwise Rotation")
    step_motor2.forward()

Hope this helps anyone.

gfabia avatar Mar 22 '21 18:03 gfabia

Well done, Glen — thank you for putting this together!

drinkspiller avatar Dec 31 '22 20:12 drinkspiller