Having played with LEDs, switches and buzzers I felt the natural next step was playing with a stepper motor or two. Unlike conventional electric motors, stepper motors allow you to rotate the axis in precise increments. This makes them useful in all sorts of Raspberry Pi projects.
Basic Stepper Motor
There is a huge selection of stepper motors to buy but I decided to experiment with a 28BJY-48 with ULN2003 control board. The reasons I chose this device where :
- It is cheap
- Runs on 5V
- Easy to interface to the Pi’s GPIO header
- Small but relatively powerful
- Widely available from both overseas and UK sellers
- Easy to obtain with a controller board
There are additional details in the Stepper Motor 28BJY-48 Datasheet.
Buy Stepper Motors for the Pi
The 28BJY-48 stepper motors can be obtained from :
Interfacing With The Pi
The stepper motor connects to the controller board with a pre-supplied connector. The controller board has six pins which need to be connected to the Pi’s GPIO header :
- 5V (P1-02)
- GND (P1-06)
and
- Inp1 (P1-11)
- Inp2 (P1-15)
- Inp3 (P1-16)
- Inp4 (P1-18)
The P1-XX references above represent the Pi header pins I used. These are defined in the Python example below in the StepPins list so if you use different pins be sure to update the Python list as well. You can use other GPIO pins if required just remember to update your Python script.
To rotate the stepper motor you provide a sequence of “high” and “low” levels to each of the 4 inputs in sequence. By setting the correct sequence of high and low levels the motor spindle will rotate. Reversing the sequence results in the direction being reversed.
Example Stepper Motor Python Script
Here is a copy of the stepper motor script I used to rotate the stepper motor. It uses the RPi.GPIO library and defines a 4-step and 8-step sequence.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970#!/usr/bin/python
# Import required libraries
import
sys
import
time
import
RPi.GPIO as GPIO
# Use BCM GPIO references
# instead of physical pin numbers
GPIO.setmode(GPIO.BCM)
# Define GPIO signals to use
# Physical pins 11,15,16,18
# GPIO17,GPIO22,GPIO23,GPIO24
StepPins
=
[
17
,
22
,
23
,
24
]
# Set all pins as output
for
pin
in
StepPins:
"Setup pins"
GPIO.setup(pin,GPIO.OUT)
GPIO.output(pin,
False
)
# Define advanced sequence
# as shown in manufacturers datasheet
Seq
=
[[
1
,
0
,
0
,
1
],
[
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
]]
StepCount
=
len
(Seq)
StepDir
=
1
# Set to 1 or 2 for clockwise
# Set to -1 or -2 for anti-clockwise
# Read wait time from command line
if
len
(sys.argv)>
1
:
WaitTime
=
int
(sys.argv[
1
])
/
float
(
1000
)
else
:
WaitTime
=
10
/
float
(
1000
)
# Initialise variables
StepCounter
=
0
# Start main loop
while
True
:
StepCounter,
Seq[StepCounter]
for
pin
in
range
(
0
,
4
):
xpin
=
StepPins[pin]
#
if
Seq[StepCounter][pin]!
=
0
:
" Enable GPIO %i"
%
(xpin)
GPIO.output(xpin,
True
)
else
:
GPIO.output(xpin,
False
)
StepCounter
+
=
StepDir
# If we reach the end of the sequence
# start again
if
(StepCounter>
=
StepCount):
StepCounter
=
0
if
(StepCounter<
0
):
StepCounter
=
StepCount
+
StepDir
# Wait before moving on
time.sleep(WaitTime)
You can download it directly to your Pi using :
wget https://bitbucket.org/MattHawkinsUK/rpispy-misc/raw/master/python/stepper.py
The script needs to be run using “sudo” :
sudo python stepper.py
Press Ctrl-C to quit.
Step Wait Time
The script adds a small delay between each step to give the motor time to catch up.In this example the default wait time is set to 0.01 seconds (10 milliseconds). To change the speed of rotation you can change this value. I found I could reduce it to 4ms before the motor stopped working. If the script runs too fast the motor controller can’t keep up. This performance may vary depending on your motor and its controller.
To specify a different wait time you can pass a number of milliseconds as an argument on the command line using :
sudo python stepper.py 20
where 20 is the number of milliseconds.
Step Sequences
The complete step sequence consists of 8 steps. If StepDir is set to -2 or 2 the number of steps is reduced to 4.
The 4 step sequence is faster but the torque is lower. It’s easy to stop the rotation by holding the motor spindle. The 8 step sequence is slower but the torque is much higher. For my turntable application I prefer the torque over speed so I will be using the 8 step sequence.
Final Thoughts
You can now control a stepper motor using a Raspberry Pi and a Python script. If you add another motor you’ve got the beginnings of a small robot!
55 Comments
As I understand it, the torque is higher when you select two inputs because you have twice the number of coils “activated”. If this is the case, then surely your 8 step sequence actually does High Torque turn (2 inputs), Low Torque turn (1 input), High Torque turn (2 inputs), Low Torque turn (1 input)…
I could be very wrong, but think you may not be using the entire torque available.
The 8 step sequence is the official one in the datasheet. The 4 step version was just one I tried because it was easy to implement in my Python script. I think what you are saying is correct but I think that’s just the way you have to drive the motor.
Can you connect more than one motor to the Pi?
Yes you can. As long as the 5V supply can provide the current and you’ve got 4 GPIO outputs to drive each motor you can add more. Technically you could add 4 using 16 GPIO pins. With a 1A rated supply you’ve got 300mA to play with on the 5V pin once the Pi has taken it’s 700mA.
Thanks for this post, I was able to get my stepper motor spinning in next to no time. I found that I needed to add a few 1/100ths of a second WaitTime or I got buzz but no spin. Probably a current thing, but for now I’m just glad to have graduated beyond flashing LEDs.
I think stepper motors sometimes need a bit of time to complete a “step” before the next sequence is applied. I’m wondering if I might need to re-introduce a small delay now that the new version of the RPi.GPIO library (0.3.1a) is much faster.
For what it’s worth, this main loop does away with the expensive array lookups. It must be more efficient because I need another 0.001 of delay
#alt. loop
mask = 3 # set to 1 or 3
while True:
# print mask
GPIO.output(24,mask & 1)
GPIO.output(25,mask & 2)
GPIO.output(8, mask & 4)
GPIO.output(7, mask & 8)
mask = (((mask & 1)<>1
time.sleep(0.003)
Meh, code got formatted. Important line should be …
mask = (((mask & 1)<<4)|mask)>>1
Pingback: Controlling a stepper motor 28BYI-48 with a Raspberry Pi | defendtheplanet.net
Hi Matt,
since you’ve commented my version of the source I just wanted to mention that the motors can get hot in case you do not switch of the GPIOs after using them
Thanks anyways, this page is a good start with stepper motors.
I noticed the opposite!
My motor’s common is hooked up to +12V and when GPIOs are off, the coils are presumably hooked up to 0V. It was very unintuitive that when my motor was not doing anything, it got super hot!
All stepper motors draw current even when they are not being used. And since your giving it an extra 7v its getting hot. The 24byj48 only use 5v.
Thanks for your stepper motor.py program, but I keep getting
File “stepper motor.py” , line 45, in
GPIO.output( xpin, True)
RuntimeError: The GPIO channel has not been set up as an OUTPUT
I have checked the wording and it is the same as yours
Any suggestions
Thanks again
Dave Barnwell
The error implies the GPIO pins are not being configured properly by lines 33-39. In your error message it says line 45 is “GPIO.output( xpin, True)”. That doesn’t appear in my script until line 68 so I suspect you’ve got some code missing from your file. I’ve uploaded a copy of the script to my Bitbucket account and updated the post to include a download link. Use wget to download this directly to your Pi and try it again.
I configured this code (Matt’s original) on the new RPi B+ and it works a dream. I then tried the speed at 0.001 and 0.0 respectively and they both worked as well. Looking for some code to go to a specific point (i.e. rotate x steps forward or reverse) and/or rotate x degrees forward or reverse, but I’ll be experimenting in the meantime. Thanks for the “start” Matt.
have you implemented that? Need some help in that
Hi Matt
thanks i try the code and i got good result . now i look for a code for controlling the number of steps .ie if i want to stop the motor after 100 rotation ,any code is available for this purpoe pls help
Great post, I got my motor going right away. I did rewrite the code to be more “pythonic” here: http://volcano.newts.org/2014/11/08/raspberry-pi-stepper-motor/
Would you just sequence the pins in reverse order to get the motor to spin in the opposite direction?
Yes that is correct.
hi there
just trying to learn Pi & python
I have this script running with the steps moving on the screen, the LED’s flashing but the motor not moving
Any idea
Dave
Hi Matt,
Am I correct in thinking that you could power the driver board of the stepper motor(s) from an independent source rather than the RPi ?
Pete
There shouldn’t be a problem with that although they would most likely need a common ground.
I have it wired properly. The lights flash in sync but the motor does not turn. And the motor is getting warm. Using the 5 volts off of the board as shown. What do I need to change?
Try increasing the time delay. Also try physically twisting the motor axle at the same time. One of my cheap stepper motors was stuck but started turning with a quick twist.
Matt, thanks for the assistance. The motor was actually moving but so slow I was not aware it was advancing. I changed the time delay and now it is moving fast. Do you recommend any sites that can expand on using this little motor? Would like code that moves fast one direction, stops and moves another direction faster. And code that takes input to start, stop and speed changes. Again, thank you for the very quick reply. I have spent a lot of time on your site due to how well you present the information to a new person. Steve, Joplin, Missouri, USA
Any reason why I can’t use an external 5V supply (keeping both grounds in common) instead of using the Pi’s? If I do, I wonder if using a level shifter between the controller board and the Pi wouldn’t be a good idea.
How can i connect 2 stepper motors on a single raspberrypi? Will I need two control boards? If so then 2 control boards can be connected to a single raspberry pi? Please repl asap. I’ve got my project to be done in 2 weeks. I’m new to raspberry pi.
You can connect two stepper motors and you would need two control boards. The second board would be connected to another 4 available GPIO pins. The Python code would need to be modified to use those 4 pins.
Thanks so much for the info and the code, got my motor going with only some minor troubles.
Some things that would have helped me:
Maybe make a note that raspberry pi 2 (and model b) have different GPIO pinouts.
Please for future people change the code so on line 37 it casts to a float rather than int. I was trying to get my motor to run quite slow so I did $sudo python stepper.pi 500.
Unfortunately as an int this evaluates to 0 and waits for 0 seconds.
Hi William, I’ve changed the GPIO pins I use in the example to avoid GPIO27 as that changes between the B and B+ configurations. I’d updated the BitBucket version of the code to fix the wait time issue but hadn’t updates the version on the page. So work ok now.
Hello Sir please help me its showing error as below
what to do
[Traceback (most recent call last):
File “/home/pi/stepper1.py”, line 20, in
GPIO.setup(pin,GPIO.OUT)
RuntimeError: No access to /dev/mem. Try running as root!]
Are you using “sudo” to run the Python script?
Hi, thanks for the info. This works great for me clockwise but I can’t get it to go anti-clockwise, could I have a faulty motor or am I doing something wrong? I have a Model B version 1.0 and am using physical pins 11, 15, 16 and 18 with step pins [17,22,23,24] in my code. I’d be grateful of any help. Thanks.
this works perfect for my setup, all i had to do was change the pin numbers. im trying to put the actual moving of the motor section into a function, wondering how easy that would be?
ive been trying:
def mover():
for pin in range(0, 4):
xpin = StepPins[pin]
print StepCounter
print pin
if Seq[StepCounter][pin]!=0:
print ” Step %i Enable %i” %(StepCounter,xpin)
GPIO.output(xpin, True)
else:
GPIO.output(xpin, False)
StepCounter += StepDir
# If we reach the end of the sequence
# start again
if (StepCounter>=StepCount):
StepCounter = 0
if (StepCounter<0):
StepCounter = StepCount
# Wait before moving on
time.sleep(WaitTime)
while True:
mover()
however this doesnt work? what needs to be passed into this function?
There’s a bug in the code where StepDir = -2. It wraps from 0 to 7 (should be 0 to 6).
Replace
if (StepCounter>=StepCount):
StepCounter = 0
if (StepCounter<0):
StepCounter = StepCount
with
StepCounter %= len(Seq)
to fix.
Also, the full step logic should have two phases on at all times (i.e. the steps with two outputs set to one). Move the two phase arrays to even slots to fix it.
Hi Will, I’ve updated the sequence so it starts with two phases energised. I’ve also corrected a bug that got the sequencing wrong for negative steps. Should work much better now.
Hi,
The cycle skips one step in the sequence. I think the = sign in line 63 is superfluous.
if (StepCounter>=StepCount):
You’re right. The sequencing was incorrect. I’ve made a few changes and it should correctly handle StepDir settings of -2,-1,1 and 2. I’ve updated the print statements to make the sequence more obvious.
You need >= because StepCount = 8 and the seq array runs 0 .. 7. So when StepCounter = 8 it will get reset to 0 to stay within the array.
Hey can you help me?
I would like to be the engine only rotates once without continuous loop.
My Speed for my Motor is:
if len(sys.argv)>1:
WaitTime = int(sys.argv[1])/float(1000)
else:
WaitTime = 0.4/float(1000)
I hope you can understand what I want .
Can you tell me what I need to change in the script ?
ps: sry for my bad englisch i come from ger.
You would need to replace the “while true” line with a for loop so it stopped moving after a certain number of steps. The while loop keeps the motor stepping through the sequence.
by using your code, yes the motor keep running forever. I’m a newbie in writing a code, so i don’t know how to ”stop” the motor as i reached the desire seq step that i want.
You would need to get rid of the “while: true” loop and replace with something that just repeated a set number of times. Perhaps a “for” loop?
In the listing above there’s an error in line 53. The if statement should start on a new line.
Somebody on the RPF forum just ran into that problem: https://www.raspberrypi.org/forums/viewtopic.php?f=91&t=132965
The script on bitbucket seems to b OK though.
Thanks Dirk. I’ve fixed it on the page. There is a strange issue with the syntax highlighter where it refuses to put a line break between those two lines.
The page still lists the if statement on the same line as xpin on this page.
There is a bug in the code formatter. I had to add a stray # character to keep the “if” on the correct line!
Hi Matt,
You Mention the pins you are wiring up:
Inp1 (P1-18)
Inp2 (P1-22)
Inp3 (P1-24)
Inp4 (P1-26)
and then use a different set in the code:
# Define GPIO signals to use
# Physical pins 11,15,16,18
# GPIO17,GPIO22,GPIO23,GPIO24
StepPins = [17,22,23,24]
Well spotted I’ve updated the text so the pins are consistent.
That is awesome! I have been looking everywhere for help on this and most things tried to push you to arduino boards, I wanted to control one motor and thought that was overdoing it. Now I get to hack this code, we are using it to control a computer science team’s vending machine.
Hello, used your code and it say this:
xpin = StepPins[pin]if Seq[StepCounter][pin]!=0:
^
SyntaxError: invalid syntax
How can i fix it? Thank you
You need to put a line break before the “if”. The code formatter on this site got confused and deleted the line break. The easiest solution is to download the file direct from my Bitbucket repository rather than cut-n-paste from the web page.
“# Physical pins 11,15,16,18
# GPIO17,GPIO22,GPIO23,GPIO24
StepPins = [17,22,23,24]”
Thank you for this guide! Can you please explain the difference between physical pins and GPIO/StepPins? pins? I can’t figure out why the distinction between them. So Physical Pin 11 is Step Pin 17? Physical Pin 15 is StepPin 22? How is this correlation established? Why aren’t physical pin and GPIO/StepPin numbers the same?
The physical pins number the pins from 1 to 40 on the header in sequence. The GPIO numbers refer to signal names coming from the Pi’s CPU. Obviously there is some room for confusion here. You can find a diagram of the Raspberry Pi header that shows both physical pin numbers and GPIO references on the Simple Guide to the Raspberry Pi GPIO Header.