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.


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.



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
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:


…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

 if speeddir>128 then  'find direction and store in direc (1 is +ve, 0 is -ve)
 'gets the speed 0-127 negative, 128-255 positive (higher =faster)

 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
   gosub moverotor  'set the rotor position for this step
 next counter

 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

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).

Quick screenshots of the PHP-to-Servo file running

23 January, 2009

The client sees the following when entering the servo control test page:


After entering some data the output of the Python script is added to the top of the page:


It’s all a bit rough around the edges but it works for now…

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…

<title>A first go at web control...</title>
if ( isset( $_POST['angle'] ) )
#Pass the variables and remove any system characters
$output=system("/usr/bin/python2.5 /var/www/cgi-bin/ 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!">

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.


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/ 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.

Python to interface with the Pololu 8-channel servo controller

19 January, 2009

I now have a Python script which is general enough to move any of the 8 servos to any angle. At the bash shell I can type:

python pos 0 90

and the ‘position’ routine will run – moving servo 0 to 90 degrees using the ‘Mini SSC II’ protocol. Along side this I produced a ‘Pololu’ protocol version running a ‘speed’ and ‘position’ routine which can be updated with further routines. I concentrated on the former for ease of debugging when things got frustrating!

The code is as follows:

import serial
import sys
#set up the serial port for action (0==COM1==ttyS0)

def setpos(n,angle):
  #Quick check that things are in range
  if angle > 180 or angle <0:
    print "WARNING: Angle range should be between 0 and 180. Setting angle to 90 degrees to be safe..."
    print "moving servo "+str(n)+" to "+str(angle)+" degrees."

  #move to an absolute position in 8-bit mode (0x04 for the mode, 0 for the servo, 0-255 for the position (spread over two bytes))


if mode=='pos':
  print "No commands given.\nUsage: pos <servo> <angle>"

Simple when you know how! And here’s the ‘Pololu’ protocol version (so far):

import serial
import sys
#set up the serial port for action
def setspeed(n,speed):
  #Quick check that things are in range
  if speed > 127 or speed <0:
    print "WARNING: Speed should be between 0 and 127. Setting speed to 1..."
    print "Setting servo "+str(n)+" speed to "+str(speed)+" out of 127."
  #set speed (needs 0x80 as first byte, 0x01 as the second, 0x01 is for speed, 0 for servo 0, and 127 for max speed)

def setpos(n,angle):
  #Check that things are in range
  if angle > 180 or angle <0:
    print "WARNING: Angle range should be between 0 and 180. Setting angle to 90 degrees to be safe..."
    print "moving servo "+str(n)+" to "+str(angle)+" degrees."

  #Valid range is 500-5500
  #Get the lowest 7 bits
  #Get the highest 7 bits
  #move to an absolute position in 8-bit mode (0x04 for the mode, 0 for the servo, 0-255 for the position (spread over two bytes))

if mode=='speed':
elif mode=='pos':
  print "No commands given.\nUsage: speed <servo> <speed>, or\n pos <servo> <angle>"

Hope this has helped someone. Don’t forget to set the jumpers to set the protocol.

Updated: Code had all the indentation removed on upload (really not a good thing for Python!). It’s still not great as it runs off the right hand side but I’ll work out showing code at some point!

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

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

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

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


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


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

#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)

#move to one position in 7-bit mode (0x02 for the mode, 0 for the servo, 127 for the position)

#move to the next

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


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!!)


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 -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!