A Python class to move the stepper motor

To properly control the stepper motor from the Raspberry Pi we need a class to represent it. This is one of the most direct ways of understanding object oriented programming (OOP): from the class you make an “object” and the object represents and controls an object in the real world (the stepper motor).

The class lets us remember (and control) all the information we need:

  • the pins the motor is connected on;
  • the speed of the motor;
  • the angle the motor is currently pointing to.

The motor seems to have 4096 positions (steps) it can point to.  These don’t correspond exactly to degrees. The class takes an angle we want the motor to point to, converts that into a step position and works out how many steps to turn it by and in what direction to get there.

Actually, it doesn’t move to the step position that is closest to the angle, it chooses step positions that are divisible by 8 because the control sequence has 8 steps in it and I didn’t want to bother starting or stopping in the middle of the sequence.

#!/usr/bin/env python

# This code is written by Stephen C Phillips.
# It is in the public domain, so you can do what you like with it
# but a link to http://scphillips.com would be nice.

# It works on the Raspberry Pi computer with the standard Debian Wheezy OS and
# the 28BJY-48 stepper motor with ULN2003 control board.

from time import sleep
import RPi.GPIO as GPIO

class Motor(object):
    def __init__(self, pins):
        self.P1 = pins[0]
        self.P2 = pins[1]
        self.P3 = pins[2]
        self.P4 = pins[3]
        self.deg_per_step = 5.625 / 64
        self.steps_per_rev = int(360 / self.deg_per_step)  # 4096
        self.step_angle = 0  # Assume the way it is pointing is zero degrees
        for p in pins:
            GPIO.setup(p, GPIO.OUT)
            GPIO.output(p, 0)

    def _set_rpm(self, rpm):
        """Set the turn speed in RPM."""
        self._rpm = rpm
        # T is the amount of time to stop between signals
        self._T = (60.0 / rpm) / self.steps_per_rev

    # This means you can set "rpm" as if it is an attribute and
    # behind the scenes it sets the _T attribute
    rpm = property(lambda self: self._rpm, _set_rpm)

    def move_to(self, angle):
        """Take the shortest route to a particular angle (degrees)."""
        # Make sure there is a 1:1 mapping between angle and stepper angle
        target_step_angle = 8 * (int(angle / self.deg_per_step) / 8)
        steps = target_step_angle - self.step_angle
        steps = (steps % self.steps_per_rev)
        if steps > self.steps_per_rev / 2:
            steps -= self.steps_per_rev
            print "moving " + `steps` + " steps"
            self._move_acw(-steps / 8)
        else:
            print "moving " + `steps` + " steps"
            self._move_cw(steps / 8)
        self.step_angle = target_step_angle

    def _move_acw(self, big_steps):
        GPIO.output(self.P1, 0)
        GPIO.output(self.P2, 0)
        GPIO.output(self.P3, 0)
        GPIO.output(self.P4, 0)
        for i in range(big_steps):
            GPIO.output(self.P1, 0)
            sleep(self._T)
            GPIO.output(self.P3, 1)
            sleep(self._T)
            GPIO.output(self.P4, 0)
            sleep(self._T)
            GPIO.output(self.P2, 1)
            sleep(self._T)
            GPIO.output(self.P3, 0)
            sleep(self._T)
            GPIO.output(self.P1, 1)
            sleep(self._T)
            GPIO.output(self.P2, 0)
            sleep(self._T)
            GPIO.output(self.P4, 1)
            sleep(self._T)

    def _move_cw(self, big_steps):
        GPIO.output(self.P1, 0)
        GPIO.output(self.P2, 0)
        GPIO.output(self.P3, 0)
        GPIO.output(self.P4, 0)
        for i in range(big_steps):
            GPIO.output(self.P3, 0)
            sleep(self._T)
            GPIO.output(self.P1, 1)
            sleep(self._T)
            GPIO.output(self.P4, 0)
            sleep(self._T)
            GPIO.output(self.P2, 1)
            sleep(self._T)
            GPIO.output(self.P1, 0)
            sleep(self._T)
            GPIO.output(self.P3, 1)
            sleep(self._T)
            GPIO.output(self.P2, 0)
            sleep(self._T)
            GPIO.output(self.P4, 1)
            sleep(self._T)

if __name__ == "__main__":
    GPIO.setmode(GPIO.BOARD)
    m = Motor([18,22,24,26])
    m.rpm = 5
    print "Pause in seconds: " + `m._T`
    m.move_to(90)
    sleep(1)
    m.move_to(0)
    sleep(1)
    m.move_to(-90)
    sleep(1)
    m.move_to(-180)
    sleep(1)
    m.move_to(0)
    GPIO.cleanup()

If you save it as “move.py” you can run it using “sudo python move.py”. It will then move the motor round by 90 degrees, pause for 1 second, move it back again and so on according to the instructions at the end of the program. Alternatively, you can import the file and use the class from another program which is what I’ll be doing soon…

Wiring done right

With the new jumper leads from eBay I wired it all up again. For the power, the motor controller board has 4 pins and I don’t know why.  Two of them are bridged by a jumper and by the other two there is a “-” and a “+” and “5-12V” underneath.  I wired the 5V pin of the Pi (P2) to the one labelled “+” and the ground pin of the Pi (P1) to the “-”.  Seems to work, but I don’t know if I just got lucky or whether it doesn’t matter which way round you put them.

For the controller lines I wired P18, P22, P24 and P26 to IN1, IN2, IN3 and IN4 respectively. That done, I turned it on and tried some software.

I think I tried the Python code on Matt’s blog first but somehow it didn’t quite work – I don’t remember  now.  What was great though was that although the motor didn’t seem to turn the LEDs came on so I had got something happening! After a bit of puzzling I realised that the LEDs are wired to the four control lines of the motor so they are very useful in diagnosing what is happening and checking you’ve got the right control sequence.

I spent some time Googling (finding Patrick’s blog), trying to understand the motor’s datasheet, and combining Matt and Patrick’s Python code to make my own version that works for me:

#!/usr/bin/env python
# Based on http://patrickcambria.com/downloads/stepper.py

# This code is written by Stephen C Phillips.
# It is in the public domain, so you can do what you like with it
# but a link to http://scphillips.com would be nice.

from time import sleep
import RPi.GPIO as GPIO
import argparse

parser = argparse.ArgumentParser(description='Stepper motor arguments')
parser.add_argument('-rpm', action='store', dest='rpm', type=float)
parser.add_argument('-revs', action='store', dest='revs', type=int)
args = parser.parse_args()

step_angle = 5.625 / 64
steps_per_rev = int(360 / step_angle)
T = (60 / args.rpm) / steps_per_rev

print "Stepping @ " + `T` + " seconds per step"

GPIO.setmode(GPIO.BOARD)

Q1 = 18
Q2 = 22
Q3 = 24
Q4 = 26

GPIO.setup(Q1, GPIO.OUT)
GPIO.setup(Q2, GPIO.OUT)
GPIO.setup(Q3, GPIO.OUT)
GPIO.setup(Q4, GPIO.OUT)

GPIO.output(Q1,0)
GPIO.output(Q2,0)
GPIO.output(Q3,0)
GPIO.output(Q4,0)

# This makes it go anti-clockwise
for i in range(args.revs * steps_per_rev / 8):
    GPIO.output(Q1,0)
    sleep(T)
    GPIO.output(Q3,1)
    sleep(T)
    GPIO.output(Q4,0)
    sleep(T)
    GPIO.output(Q2,1)
    sleep(T)
    GPIO.output(Q3,0)
    sleep(T)
    GPIO.output(Q1,1)
    sleep(T)
    GPIO.output(Q2,0)
    sleep(T)
    GPIO.output(Q4,1)
    sleep(T)

GPIO.cleanup()

Lines 12-15 are just dealing with command line arguments: you can tell it how many times to turn round with “-revs” and how fast to go with “-rpm” (revolutions per minute).  I found that 10rpm was about as fast as it can go.

Lines 23-38 set the pins up using the pin numbers from the Raspberry Pi’s circuit board (starts 1 bottom left, 2 top left and goes up from there).  It sets them to be outputs and sets them to zero (0V I assume).

Lines 41-57 actually encode the sequence of ons and offs given in the motor’s datasheet and loops round it the right number of times.  Finally, it sets all the pins to zero again. I found it made the motor turn anti-clockwise, though the datasheet says the sequence is for clockwise rotation – perhaps it is because I plugged the 5V and ground pins in the wrong way round?

I called it “spin.py”.  To run it you have to type “sudo python spin.py” because you have to have root permissions to access the GPIO port.

Once this worked I started trying to do something more sophisticated but quickly realised that I needed to make a class to hold the state of the motor.  More on that next time…

Stepper motor

Two motors, just in caseTo make the Where Clock I need a stepper motors. I don’t know how stepper motors work but I know you can get them to turn around to point in a particular direction. Of course, I’m not the first person to think of this and after a bit of Googling I found a page about using stepper-motors from a Raspberry Pi.

That blog suggested a 28BJY-48 with ULN2003 control board which can be bought from eBay from “4tronix_uk” so I went ahead and bought 2 (just in case I needed another for something…). The motors arrived quickly and I took one, along with a Pi into Maplins to get some wires to join them together. Unfortunately, Maplins do not have the female to female jumper leads you need to connect the Pi directly to the controller board of the stepper motor. Instead I was sold some male-female leads and an IDE cable! The IDE cable fits onto the GPIO pins of the Pi (well, it hangs over the side) and we thought I could stick the male end of the jumpers into the other end of the IDE cable and then the female ends onto the motor’s board.

All wired up
Following the instructions about which GPIO pin is which on the same blog as before (mine is a revision 2 Pi), I cautiously wired it all up.  It was extra complicated because I had to also count along the holes in the IDE cable.  I then copied the code from the first post crossed my fingers and ran it.

Well, it didn’t work.  I got out the multimeter and checked there was the right voltage on the pins on the Pi (for instance, 5V between pins 2 and 6 and 3V between 2 and 1) but I could find no reading at the other end of the cabling.  I think perhaps that the male pins on the jumper leads weren’t quite long enough to connect to the IDE cable.

So, back to the drawing board, or rather eBay, where I purchased a bunch of female to female jumper leads from “phenoptix“.  They were listed as being for the arduino, but are just fine and arrived the next day…

The start of something

I’ve been thinking about having a blog for a long time as a place to put information that other people might find useful. Finally I’ve got one (it is very easy to set up thanks to my host, Webfaction). I suppose I finally thought I had something interesting enough to share.

I bought a couple of Raspberry Pi computers a few weeks back (from ModMyPi), not with any concrete plans as to what to do with them but I felt sure that they’d be useful for something (print server, file server, XBMC system for behind the telly, programming fun for the children, etc…). Well, somehow at work I got to reading about the Internet of Things (IoT) which is a buzz word these days. I think perhaps this page was linked to from the BBC R&D blog? Anyway it’s a great read and has a “Where Dial” mentioned as one of the interesting internet-connected “things” that are possible these days.  The Where Dial is inspired by the clock in the Weasley’s house in the Harry Potter books.  It doesn’t tell the time, but instead has a hand for each member of the family that moves round to point at various states such as “travelling”, “hospital” or “mortal peril”.

So, I thought I’ve got a Raspberry Pi, I’ve been wanting to hook it up to some hardware and I’ve also wanted to have a play with Android software, particularly an interesting event-driven system for Android called on{X} from (would you believe) Microsoft.

Why not make my own Where Dial, or as I am calling it, a “Where Clock”?  Perhaps the “mortal peril” state can be activated if the accelerometer goes above 1g?