Add Stepper Motor
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
More info here: http://www.winkleink.com/2013/04/raspberry-pi-unipolar-stepper-motors.html
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?
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
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.
Well done, Glen — thank you for putting this together!