Bipolar stepper motor control with Picaxe and L293D chips

28 January, 2009

I’ve now got a schematic and program for running a bipolar stepper motor via a serial interface (just as for the unipolar case). This is important for the robot arm cause because two of the three steppers will be of the bipolar kind.
Where driving the unipolar stepper required only current ‘pushing’ (ie all in the same direction through a common ground), a bipolar motor requires current to flow in both directions. The following image shows the difference.

steppers

In theory the control of this is very easy with two half H-bridges (essentially four transistors) controlled by several Picaxe output lines). The basic concept is shown below.

image

(Source: http://commons.wikimedia.org/wiki/File:Bipolar_Stepper_Motor_H-bridge.png)

The L293D chip has the requisite transistors and a nice interface (with lots of extra features that my DIY skills couldn’t match). I’ve also used it in the mobile robot experiment to control the direction of 2 motors.

As with anything like this though, one needs to think through the sequence and outline a table of output values corresponding to the steps, as below.

Direction Coil 1 Coil 2
N + 0
NE + +
E 0 +
SE +
S 0
SW
W 0
NW +

At first I was using 6 output lines (!) – 4 for each of the transistor inputs and 2 for enabling or disabling the L293D’s 2 ‘half h-bridges’. In this configuration it could be reduced to 4 by using an external NOT gate (because 2 of the controls are always the opposite of another 2). Such a scheme would look like this:

Direction EN1-2 1A 2A EN3-4 3A 4A
N 1 1 0 0 0 0
NE 1 1 0 1 1 0
E 0 0 0 1 1 0
SE 1 0 1 1 1 0
S 1 0 1 0 0 0
SW 1 0 1 1 0 1
W 0 0 0 1 0 1
NW 1 1 0 1 0 1

But while trouble shooting this (I had swapped LSBs with MSBs so it probably did work after all!) I came across a site tying the enable lines Hi and using 4 control lines. At the poles one of the loops must have zero current flowing through it. In the above scheme this is done by setting the enable line Low and ignoring the A values. In the 4 line scheme this can be achieved by sending the same control output to both ends of the loop (ie the potential difference across the coil is 0). Then the table looks like this (including half stepping):

Direction 1A 2A 3A 4A
N 1 0 0 0
NE 1 0 1 0
E 0 0 1 0
SE 0 1 1 0
S 0 1 0 0
SW 0 1 0 1
W 0 0 0 1
NW 1 0 0 1

That makes the schematic as follows:

bipolar_schematic

…and the program as follows. Note that this program is designed to wait until it detects a serial input on input 0 (pin 17) in the form of three bytes: The first is a qualifier – 85 uniquely identifies this stepper motor – the second is the number of steps – 0-255, and the third is the speed – 0-127 is backwards, 128-255 is forwards.

'Serial driven stepper motor by DMT195

symbol posrotor=b0
symbol numturns=b2
symbol speeddir=b7
symbol direc=b8
symbol pulsegap=b3
symbol counter=b4
symbol modrotor=b1
symbol outbyte=b5

posrotor = 1 'set starting position

start:    'main sequence - wait for command then move the motor
 numturns=0   'sets a default number of turns
 high 7    'reset ready flag
 serin 0,T2400,(85),numturns,speeddir 'get serial data
 gosub getspeed   'get the direction and speed
 gosub move       'perform number of steps
goto start

getspeed:
 if speeddir>128 then  'find direction and store in direc (1 is +ve, 0 is -ve)
  direc=1
 else
  direc=0
 endif
 speeddir=speeddir//128
 'gets the speed 0-127 negative, 128-255 positive (higher =faster)
 pulsegap=264-2*speeddir
return

move:
 for counter=1 to numturns
  pause pulsegap      'wait before moving again (set by speed)
 if direc=1 then
  posrotor=posrotor+1 'increase/decrease the step by one
 else
  posrotor=posrotor-1
 endif
   gosub moverotor  'set the rotor position for this step
 next counter
return

moverotor:
 modrotor=posrotor//8    'find out where the rotor arm should be (1 of 8 positions)
 lookup modrotor, (0x08,0x0A,0x02,0x06,0x04,0x05,0x01,0x09), outbuyte
 '(%00001000,%00001010,%00000010,%00000110,%00000100,%00000101,%00000001,%00001001) is (0x08,0x0A,0x02,0x06,0x04,0x05,0x01,0x09) in hex
 'looks up the step from the sequence and applies it to the output pins
 pins=outbyte
return

Please leave comments if you’ve found this useful or if you think I’ve made a mistake.

I have something to say about accessing all these devices through a single serial port too but that will have to wait for one or two more tests…

Update: Of course this can be done with a PICAXE 08(M)  with the 4 outputs and 1 serial input to spare. The pinouts would have to change as would the respective program. If there’s enough demand for me to document this I will. Let me know in the comments. Consider using this little board from Technobots.

Update 2: I’ve reduced the code length by using a lookup table instead of the select/case structure. I’ve also use hex to reduce the size of that line (but included the binary values in a comment below for those that are interested).

Advertisements

Running the Python script over PHP5

19 January, 2009

It took some time to get this working so I’m quite proud that it does actually work now. There were a couple of subtleties that needed ironing out and I want to share them for those trying to do something similar. Here’s the code and then I’ll note a few lines in detail after…

<html>
<head>
<title>A first go at web control...</title>
</head>
<body>
<?php
if ( isset( $_POST['angle'] ) )
{
#Pass the variables and remove any system characters
$angle=escapeshellcmd($_POST['angle']);
$servo=escapeshellcmd($_POST['servo']);
$output=system("/usr/bin/python2.5 /var/www/cgi-bin/controlsscii.py pos  $servo $angle");
}
?>
<form action="page1.php" method="POST">
Input area:<p>
Servo number (0-7 limited resolution, 8-15 full sweep):
<input type="text" name="servo"><p>
Value (0-180 degrees):
<input type="text" name="angle"><p>
<input type="SUBMIT" name="submit" value="Try it!">
</form>
</body>
</html>

So firstly I had an issue with PHP going from v4->v5. Before the change variables were passed from forms with method=”POST” directly into variables. Now one has to access the $_POST environmental variable. escapeshellcmd() is used to ensure arbitrary scripts can’t be run.

$angle=escapeshellcmd($_POST['angle']);

The main problem I came across was getting the paths correct to the call to python. The correct eventual line is:

$output=system("/usr/bin/python2.5 /var/www/cgi-bin/controlsscii.py pos  $servo $angle");

I initially had the full path to the script but not that of python itself. An exhaustive web search and eventual examination of the Apache error log helped me narrow down this and subsequent problems.

The final tweak I had to make was to the serial port file (/dev/ttyS0) – I had to set the correct permissions such that the script could access it.

Updated: The code wasn’t formatted well enough for my liking. Some of it may go out of bounds. I will work this out, I’m sure.


Controlling servos using Ubuntu (linux server) and Python

17 January, 2009

As I mentioned previously, I bought one of these servo controllers from Pololu. It’s capable of controlling up to 8 servos with instructions from either a microcontroller using TTL (via one of the headers) or over RS232 from a computer. I want to build a robot arm which is controlled over the web. Therefore I want my Ubuntu server to interface with this controller.

I’m not very good at the low level programming stuff and the documentation isn’t all that great for hobbyists (in my opinion) so I had some fun trying to get it all to work.

So here’s the first part of my story/tutorial based on my experiences using python on linux to control a servo…

Protocol info

OK, so this device comes with a manual describing two types of protocol it supports; SSCII and Pololu. SSCII looks the most straight forward but it’s limited in a few ways. The worst part is that you can only use 7-bit precision when placing the servos and you can’t change speeds. Saying that if it suits your needs then go ahead. It needs three bytes of information: A constant (tells the thing you’re talking to it and not something else!), a servo number, and a position (0-127 representing 180 degrees). The following will move servo 0 to position 84 (0xFF being hex representation of the constant byte):

0xFF, 0, 84
11111111|00000000|01010100

A handy tip is that if you need to convert, Google is able to go between decimal, hex, and binary for you. Simply type “0xff in binary” into the search box!

The Pololu protocol allows different speeds for different servos, 8-bit precision, and setting neutral positions too. For that reason it’s too much for me to outline fully here. Check the manual (pdf) out for further information.

I will describe the two commands which I use below. The first is to set the speed of a servo before moving it. It consists of 5 bytes; a control, a board identifier (if you’re only using one board this never changes either), a label for the command (0x01 is for speed), a servo identifier (0-8), and a speed value (1 is the slowest, 127 is the fastest, 0 is uncontrolled – ie as fast as it will go):

0x80, 0x01, 0x01, 0, 127
10000000|00000001|00000001|00000000|01111111

The second is for 7-bit precision positioning (I said there is higher precision than this but that uses a different mode to this). It too consists of 5 bytes; a control, a board identifier, a label for the command (0x02 is for 7-bit positioning), a servo identifier, and a position (0-127):

0x80, 0x01, 0x02, 0, 127
10000000|00000001|00000010|00000000|01111111

Programming info

In linux serial ports are labelled as ttyS0 to ttySx, where x is the highest available. These are equivalent to the COM1, COM2, etc you see labelled in Windows. I was instantly pleased that to find that a command from the shell such as:

echo “this info will go down the serial line” > \dev\ttyS0

sends ASCII data, character for character, down the serial line. I thought things were going to be simple as PHP can issue the command “exec(some shell command)” – instant web controlled hardware!

I was lucky enough to have with me a cheap oscilloscope (USB DAQ from Measurement Computing) to monitor the output from the relevant pin of the serial cable (pin 2 – rx, although I think it should have been pin 3 – best to check!).  I quickly discovered that I couldn’t send the correct bit sequence down the serial line required by the Pololu controller. I could have found the ASCII equivalents for the header digits (the 0x80 and 0x01, etc) but when it came to sending data values for positions this would be difficult. Who knows, this may be good enough for someone out there. It was good for testing purposes if nothing else.

I then weighed up my options and decided to use Python to program in. This was because it is high level, has some nice serial commands via pyserial, and I’ve heard loads about it. It is supposed to be easy to do complicated things and there’s plenty on the web about it. It’s open source and much of the linux community use it. If you’re running linux you probably have it. Go to a command line and type “python”. This should then give you a shell type environment to enter python commands. To quit type “quit()”.

So after downloading and installing Pyserial (see the instructions on the pyserial web site) I started testing the commands.

At the start you have to tell Python to use Pyserial:

import serial

And then

ser=serial.Serial(0)

sets the serial port to use (ttyS0, or COM1 in this case) and assigns it a handle. Now I can write things to it using:

ser.write(“things in here”)

which is equivalent to the Bash, shell way of doing things. But you can assign values to variables and say

ser.write(variable)

This is really handy because you can assign ‘variable’ the correct value based on what you’re trying to do, and even concatenate (string together) multiple values. Also by using the command ‘chr()’ one can input decimal and hexidecimal values in and they will be converted to ASCII for transmission. So my simple program is shown below. It sets the speed of a single servo (servo 0) to maximum, moves it to a position, and then to another after a small wait (5 seconds).

import serial
import time

#set up the serial port for action
ser=serial.Serial(0)
ser.baudrate=2400

#output some data (qualifiers first)

#set speed (needs 0x80 as first byte, 0x01 as the second, 0x01 is for speed, 0 for servo 0, and 127 for max speed)
bud=chr(0x80)+chr(0x01)+chr(0x01)+chr(0)+chr(127)
ser.write(bud)
time.sleep(1)

#move to one position in 7-bit mode (0x02 for the mode, 0 for the servo, 127 for the position)
bud=chr(0x80)+chr(0x01)+chr(0x02)+chr(0)+chr(127)
ser.write(bud)
time.sleep(5)

#move to the next
bud=chr(0x80)+chr(0x01)+chr(0x02)+chr(0)+chr(0)
ser.write(bud)

This file is saved as control.py and then run from the command line using the following:

python control.py

The only confusion I have is that the servo seems to ‘bounce’. It goes to the first position fine, waits, then seems to bounce off one position to get to the second. Not sure I understand this – it could be that the centre hasn’t been set yet. I will iron this out soon, I’m sure. (Update: The servos didn’t have enough power. I have the jumper js=jcc on which meant that the servos were pulling current away from the electronics, making it fail. I fixed this by sourcing my power off a molex connector from the server. Works well!!)

Concluding

Obviously this is just setting up the basics and I have a long way to go before it becomes useful but it does show that my server can control servos! What’s also cool is that I’ve been using three PCs to do it! One is the server, the second is a work laptop with a USB data acquisition board for debugging, and the third is a windows PC (my HP mini-note 2113) for writing code and running commands over SSH. This is handy because my server doesn’t have a monitor and it allows me to develop in a user friendly environment (ie not all on the command line!). So I use Notepad++ (which is great for a lot of things – HTML, CSS, PHP, etc) to edit the Python code and Putty (which is a windows SSH client) to control the server and run the file.

Next step is to make lots of functions and have them called from the command line (eg “python control.py -move 0 123” or similar). I can then write a PHP web page which can call them with some (filtered!) user input.

I hope this has been useful. I had a hard time starting out with serial comms so hopefully I can save someone else that initial pain!


Tasks for 2009

16 January, 2009

My brain is a muddle with so many things I want to start and finish so I’ve been writing my priorities down for my robotics endeavours in 2009. I thought I’d share them since progress in all will be documented in this blog.

  1. Blog more! I’ve got lots of things going on in my head and I really must capture them – for my own good over anything else. I’ve also got a notebook so I shall summarise progress and thoughts from that on this site.
  2. Get my robot arm up and running. This breaks down into several areas:
    It will be based on 3 stepper motors (1 unipolar, 2 bipolar) and a servo or two for the grip. I’ve sorted the unipolar, I know how to do the bipolar (I found a half written blog post about it!) and just need to implement it, and then there’s the servos.
    image
    I bought a serial controlled (proper rs232 and everything) Pololu 8 channel servo controller just before I had my long break. I want to get that working from a PC. This will also supply pass-through TTL to my other (home made) controllers. I need to learn how to program it. I ultimately want the arm connected to my linux server and controlled over the web! I’ve learned how to go from PHP to scripts so it’s just a matter of tinkering.
    Next is the mechanical side of things. I need to design the pieces and cut them out of my acrylic sheet. The design is a big nebulous at the moment so I’m a little concerned about doing a decent job.
    Putting it all together!
  3. Get hold of an Arduino to have a play. These seem cheap, well put together, well documented, open source, and with a vibrant community. They also seem like a handy tool for generating voltages, doing data acquisition tasks, etc. I wanted to write a table of the various microcontrollers and their relative merits. If I did then this baby would do very well in most categories. If you’re reading this and just starting out with hardware control and microcontrollers then it’s a good first port of call.
    image
  4. Other stuff. I have the odd idea which sounds simple but would probably take all year. Such as… building a GPS logger for my car. I’m just intrigued to see a year’s worth of driving overlaid on Google maps or similar!

The easy way

13 January, 2009

I’m keen to get back into robotics now that my PhD is nearing an end (although it’s not over yet :-|). I’ve been researching a lot to work out the best thing to attack to make the most effective use of my time. The last thing I did was to buy some acrylic sheeting to start making stuff – possibly an arm.

Anyway, I came across this stepper motor driver on my travels. Sparkfun is quite a well known company in the hobbyist electronics market so I’m surprised I didn’t see it when I was researching my stepper motor controllers. It has three great things going for it: i) it’s tiny, ii) it looks really simple to get running with, and iii) it’s dirt cheap <$15 (or £10).

image

I thought I’d mention it if someone’s hunting for such things.


PICAXE unipolar stepper motor controller

8 March, 2008

Here’s my stepper motor controller, based on an 18 pin PICAXE microcontroller. The idea is that this chip takes away the processing overhead associated controlling a stepper. A master CPU (of some kind, potentially a PC) can send a serial command to the PICAXE telling it how much to move and at what speed (and direction). A Darlington driver array controls the power and a 7-segment display shows the current coil powering. Here’s the schematic (PICAXE programming connector not shown):

schematic

The way I’ve programmed this is different to other approaches I’ve seen. These tend to complete a full rotation every time. I’m sure others have done controllers that manage sub-rotation control like this but maybe not the way I’ve done it. I basically have a single value which defines how far the motor has rotated. I then increase this number step wise and use modulo division (//) to find out what position the rotor should have.

Program and video of operation after the break…

Read the rest of this entry »