Building a Linux-controlled walking robot

ArticleCategory: [Es gibt verschiedene Artikel Kategorien]

Hardware

AuthorImage:[Ein Bild von Dir]

[Photo of the Authors]

TranslationInfo:[Autor und Übersetzer]

original in en Katja and Guido Socher 

AboutTheAuthor:[Eine kleine Biographie über den Autor]

Katja is the German editor of LinuxFocus. She likes Tux, film & photography and the sea. Her homepage can be found here.

Guido is a long time Linux fan and he likes Linux because it is designed by honest and open people. This is one of the reasons why we call it open source. His homepage is at linuxfocus.org/~guido.

Abstract:[Hier sollte eine kleine Zusammenfassung stehen]

In this article we build a small six-legged walking robot which we then control with a Linux PC using the parallel port interface.
Other devices can be controlled similarily with the parallel port.

ArticleIllustration:[Das Titelbild des Artikels]

[Illustration]

ArticleBody:[Der eigentliche Artikel. Überschriften innerhalb des Artikels sollten h2 oder h3 sein.]

Introduction

Robots have always fascinated us and so we were both excited when we found a book about robots some time ago which already included the robot kit to build a small insectlike robot called Stiquito. Stiquito is a somewhat special robot because it doesn't have a motor but walks because the legs are wired with nitinol and this way it walks totally silently just like a real insect. But when we had built it we noticed that because there wasn't enough friction with the surface where it walked on, its actual movement was very very slow. Fortunately the book also included some descriptions of other robot designs which finally inspired us to build the robot you can read here about.

Nitinol

Our robot is motorless and walks because the legs are wired with and powered by nitinol. Nitinol is a shape-memory alloy actuator wire made of nickel and titanium that contracts like muscles when electrically heated. When conducting an electric current, the wire heats and shortens (it returns to its "unstretched" shape). A counterforce (music wire in our case) is then required to return the nitinol to its original length. When you stretch the wire only 3 to 5 percent the muscle wire is very consistent and reliable and can run for millions of cycles.


Building the robot

To build the robot we used the following parts:



[pliers]
Fig 2: needle-nose-pliers
You should be able to get most parts in a good hobby store where parts for small air planes, remote controlled cars etc... are sold. If you can't get them in your hometown look where the nearest university with an architecture department is located. As the students usually have to build models of houses and other buildings you will very likely find a shop there where you can buy such things as the very thin brass tubes.
When buying the needle-nose-pliers make sure that the pliers are flat-faced, otherwise you will not be able to crimp the nitinol wire.


[parts]
Fig 3: The main parts for the robot.

Building the body

For the body you first need three parts of the circuit board, the one with 6x6 holes and the two with 6x7 holes as well as 4cm of 2 mm diameter brass tube together with 3.7 cm music wire.

[hinge]
Fig 4: backbone and powerbus

Cut the brass tube in peaces of 8, 17.5 and 8mm as shown in the picture. You can do this by rolling it back and forth under a sharp kitchen knife and then bending it. The tubes will break where you made the notch with the knife. It is important that the tube in the middle is slightly longer than the 6x6 holes circuit board. Cut off about 3.7cm of music wire. The final length must be around 3 mm longer than the 3 tubes together. Put the music wire through the three tubes
The tube in the middle must be able to rotate while the other two are soldered to the music wire.

[solder the 3 body parts]
Fig 5: solder boards to backbone

The tube in the middle is now soldered to the 6x6 holes circuit board. Take care that it can rotate. The other two tubes are soldered to the other two circuit boards.
Now take the small, 2x7 holes, circuit board. It should stand up on edge from the middle brass tube. The circuit board must be notched with a little file or with the cutter. Solder it to the middle brass tube and the middle circuit board as shown in the picture:
[the flag]
Fig 6: adding the small circuit board

Sand the 1mm brass tube and cut several 4mm long pieces of the tube. Roll the tube under the kitchen knife and then bend it. You need 16 of those crimps but make a few spare crimps.

As a lot of crimping is needed now you should better test it with a little bit of nitinol before you start: Put the end of the nitinol wire into the very thin brass tubes (1mm diameter outside) and then squeeze the brass tube with the needle-nose-pliers. This is called crimping. Take care to buy good needle-nose-pliers because the forces needed to squeeze the brass tubes are very high. You can also slide the ends of the nitinol through 600 grid sand paper to get good electrical connections.

Now we will wire the nitinol wire that is needed to move the legs up and down.

[the bridge]
Fig 7: "the bridge"

You span the nitinol wire so as if you wanted to build a bridge. You start on one side. There you put the nitinol wire through the last hole that is possible on the left and straight side. You tie a knot in the nitinol wire (in order to insure a better connection) and put a crimp ( a ca. 4mm brass tube) over it and crimp it tight so that it is tight and the nitinol wire can be put through the second hole from above on the left side and then through the last possible hole on the left and straight side. On the bottom again a knot is tied in the nitinol wire and a crimp is put over and crimped tight (see Fig 7). The nitinol wire must be tight but not too much. If you tip with the finger on it then it should move 2-4 mm. If it isn't tight enough or if it is too tight then the robot will not move properly later on. Solder the crimps to the board.
Do the same on the second side.
Before continuing try out if it works. Use a 1.5V AA mignon battery and connect it to one of the nitinol wires. When the wire contracts the middle body part must rotate by 10-20 degrees. Take care not to connect the battery longer than 1 second. You damage the wire if you overheat it.

The legs


[music wire for legs]
Fig 8: bend the wire

For the legs you cut three 10cm long parts off the music wire. Each is bend 1.5cm on both sides. Then they are soldered to the three body parts of the robot. They should be parallel to each other.

[legs top view]

[legs bottom viewl]
Fig 9, 10: legs on the robot

Now you must wire the nitinol wire to the 6 legs.

[leg and nitinol]
Fig 11: add the actuators

Put nitinol wire from above through a crimp and through a hole in the circuit board. The distance to the music wire is 3 holes. Crimp it tight (see pictures above).
Then pull a crimp over the music wire until you reach the knee bending. Put the nitinol wire through it and crimp it tight. Now comes the most difficult part. Hold the robot with a little vice and fix and bend the legs with tape or extra cooper wire. The music wire acts as a counterforce to the nitinol. For this to work the nitinol must not be loose at all. The music wire must be pulled by 1 circuit board hole towards the nitinol and then the crimp must be soldered to the leg.
[nitinol must not be loose]
Fig 12: nitinol and music wire on the same level


Make sure that the music wire and the nitinol are on the same level. The legs must not move up or down when the nitinol contracts. The leg must move backwards.

Do the same with the other five legs.
The legs and the music wire with the brass tubes in the middle of the robot act as a power bus and therefore there must be an electrical connection between all of them. As however the middle body part has more freedom because it can rotate and therefore has no good connection we improved this by taking 3 cm of the 0.1mm varnished cooper wire and wraping it around the spare brass tube to get a little coil. Take out the brass tube and then solder this coil in the middle to the inner leg pair and to one of the outer leg pairs. The coil shape of the wire ensures maximum flexibility.

When the robot is ready you can solder 0.5m long pieces (or longer if you want) of 0.1 mm varnished cooper wire to the crimps on the board and solder the body crimps themself to the circuit board. We need 9 wires, 6 for the legs, 2 for up/down and one for the common powerbus. You can solder the other ends of the wires to a small connector which you can then plug into a corresponding small socket on the driver circuit.

The gait

Our insect is designed to walk in a tripod gait. A tripoid gait means that 3 legs are on the ground (two on one and one on the other side) while the other 3 are up in the air. When the robot walks then the 3 legs on the ground move in one direction while the legs in the air walk in the opposite direction.
[The gait]
Fig 13: The gait



Controlling the robot with your Linux computer

The driver circuit

This circuit board allows you to use your PC to control the actuators on the robot and plugs into the parallel port.
When we developed our computer program we first tested it with the LEDs and only pluged the robot control wires into the socket on the board when it worked correctly and the LEDs showed a correct working gait. You should do the same when you experiment with the program.
The robot is quite hungry. You need to run between 200 to 250 mA of current through the nitinol to contract it. The 3 cm long nitinol wires on the legs have about 7 Ohms. Always start the software first before you connect the power to the driver circuit because all data pins are at first set to off by the software to prevent damaging the nitinol wire. As the bios of the computer sets the parallel port data pins to random values some of them are maybe in the state on and the nitinol can be damaged if you run the current for much longer than 1 second through it. The time for the nitinol to cool down should be 1.5 times the time you heated it.

The circuit diagram:
[circuit]
Fig 14: circuit diagram

As you can see in the diagram above we use an electronically stabilized power supply. This is to ensure good and stable power and to protect the parallel port. As external power supply you can connect any DC power supply between 6 and 24 V. The 7805 is a standard voltage regulator. The only thing to pay attention to here is that the 2 capacitors (470uF and 0.1uF) are located very closely to the 7805 voltage regulator because otherwise it could happen that the 7805 chip starts to oscillate which could destroy the 7805.

The actual driver has to be build 8 times. One for each leg and 2 for twisting the robot (moving the legs up and down). We use a small NPN Darlington transistor because our robot needs a lot of current. The BC875 or BC618 can switch about 500mA. The 47K on the input ensures that an open circuit (e.g the computer is not connected) is always equivalent to "off". The voltage level on the parallel port is above 4V for "on" and below 1V for the state "off". The transistor works only as a switch. The 15 Ohm resistors limit the current and protect both the legs of the robot and the transistor. The LEDs show the state (on or off).

Below you see pictures of the circuit. The red LEDs (the ones which are parallel to the robots actuators) are difficult to see as we used transparent red LEDs. We built the 15 Ohm resistors from constantan wire coils but this was just because we had plenty of that wire. It is cheaper to buy ready made 2W resistors.


[the final drivercircuit1] [the final drivercircuit2]
Fig 15: the circuit



The parallel port

The parallel port was designed to serve as an output port from a personal computer and to attach to a printer. Some parallel ports allow both input and output. Here we only use the port for output. In a later article we will connect sensors to the robot and then also use the input lines. Although there are 25 pins for the parallel port, we only use nine. Eight of the lines are used as data output lines and one line serves as the electrical ground.
The pinout for the parallel port is as follows:

25 PIN D-SUB FEMALE at the PC.

 Pin  Name   Dir   Description
 1  STROBE  [-->] Strobe
 2  D0      [-->] Data Bit 0
 3  D1      [-->] Data Bit 1
 4  D2      [-->] Data Bit 2
 5  D3      [-->] Data Bit 3
 6  D4      [-->] Data Bit 4
 7  D5      [-->] Data Bit 5
 8  D6      [-->] Data Bit 6
 9  D7      [-->] Data Bit 7
 10 ACK     [<--] Acknowledge
 11 BUSY    [<--] Busy
 12 PE      [<--] Paper End
 13 SEL     [<--] Select
 14 AUTOFD  [-->] Autofeed
 15 ERROR   [<--] Error
 16 INIT    [-->] Initialize
 17 SELIN   [-->] Select In
 18 GND     [---] Signal Ground
 19 GND     [---] Signal Ground
 20 GND     [---] Signal Ground
 21 GND     [---] Signal Ground
 22 GND     [---] Signal Ground
 23 GND     [---] Signal Ground
 24 GND     [---] Signal Ground
 25 GND     [---] Signal Ground
You connect the driver circuit to pin 18 (GND) and to the data pins (2-9).

The parallel port normally works such that one byte is written to the data lines and then the computer sets the strobe line to 1 to indicate to the printer that the data lines are valid now. We use the data lines directly because so we don't need to use an extra logic for the strobe line.

The software

You can download the software from > here <.
Extract it with the command tar zxvf pprobi*.tar.gz. Installation instructions are included.

The program is written in C. With the arrow keys and the space key you can control the robot, making it walk straight, right, left and backwards. Use the space key to stop the robot and q (or x) to quit the program. Instead of the arrow keys you can also use the h,j,k,l keys (vi key mapping) if you like. The values we used for the leg movements are optimized for our robot. Each robot is a bit different, mainly because it is difficult to get the same tension on all nitinol wires. Your robot will work with the software as it is but not all legs may move by the same amount and you will have to experiment a bit until you find the values that suit your robot best. Just take care that none of the legs ever gets overheated or doesn't get enough time for cooling down again.

==== pprobi.c =====
/* vim: set sw=8 ts=8 si : */
/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License.
 * See http://www.gnu.org/copyleft/ for details.
 *
 * Written by Katja Socher <katja@linuxfocus.org> 
 *         and Guido Socher <guido@linuxfocus.org>
 *         
 */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>
#include <signal.h>
  
#include "robi.h"

/* ----------- */
static int opt_r=0;
static int fd=0;
/* ----------- */
/* ----------- */
void help()
{
    printf("pprobi -- control software for a walking robot\n\
USAGE: pprobi [-h] [parport-device]\n\
\n\
OPTIONS:\n\
         -h this help\n\
         -r reset the parallel port data pins (all zero) and exit\n\
     \n\
The default device is /dev/parport0 \n\
");
#ifdef VERINFO
    puts(VERINFO);
#endif   
exit(0); 
}
/* Signal handler: all off then exit */
void offandexit(int code)
{
    robi_setdata(fd,0);
    set_terminal(0);
    exit(0);
}

/* ----------- */
int main(int argc, char **argv)
{
    int state,bpat,alternate;
    char *dev;
    /* The following things are used for getopt: */
    int ch;
        extern char *optarg;
        extern int optind;
        extern int opterr;

    opterr = 0;
    while ((ch = (char)getopt(argc, argv, "hr")) != -1) {
        switch (ch) {
        case 'h':
            help(); /*no break, help does not return */
        case 'r':
            opt_r=1;
            break;
        case '?':
            fprintf(stderr, "serialtemp ERROR: No such option. -h for help.\n");
            exit(1);
        /*no default action for case */
        }
    }
    if (argc-optind < 1){
        /* less than one argument */
        dev="/dev/parport0";
    }else{
        /* the user has provided one argument */
        dev=argv[optind];
    }
    fd=robi_claim(dev); /* robi_claim has its own error checking */
    /* catch signals INT and TERM and switch off all data lines before
     * terminating */
    signal(SIGINT, offandexit);
    signal(SIGTERM, offandexit);

    /* initialize parpprt data lines to zero: */
    robi_setdata(fd,0);
    set_terminal(1); /* set_terminal has its own error handling */ 
    state=0;
    alternate=0;
    if (opt_r){
        offandexit(1);
    }
        while(1){
        ch=getchoice();
        if (ch!=0) state=ch;
        if (ch == ' '){
            printf("Stop\n");
            robi_setdata(fd,0);
            usleep(500*1000);
        }
        if (ch == 'q'|| ch == 'x'){
            printf("Quit\n");
            break;
        }

        if (state=='l'){
            /*right */
            printf("walking right\n");
            walkright(fd);
        }
        if (state=='h'){
            /*left */
            printf("walking left\n");
            walkleft(fd);
        }
        if (state=='j'){
            printf("walking back\n");
            walkback(fd);
        }
        if (state=='k'){
            if (alternate){
                printf("walking straight on a\n");
                walkstraight_a(fd);
            }else{
                printf("walking straight on b\n");
                walkstraight_b(fd);
            }
            alternate=(alternate +1) %2;
        }

    }
    /* we get here if q was typed */
    set_terminal(0);
    return (0);
}

==== robi.c  =====
/* vim: set sw=8 ts=8 si : */
/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License.
 * See http://www.gnu.org/copyleft/ for details.
 *
 * Written by Katja Socher <katja@linuxfocus.org>
 *        and Guido Socher <guido@linuxfocus.org> 
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h> 
#include <sys/types.h>
#include <sys/time.h> 
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <linux/ppdev.h>
#include <sys/ioctl.h>
#include <termios.h>
#include "robi.h"


/* like printf but exit the program */
static int die(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    vprintf(fmt, ap);
    va_end(ap);
    exit(1);
}
/* get one character from stdin 
 * Returns non zero if char was read otherwise zero
 * The arrow keys are mapped as follows:
 * <- = h
 * -> = l
 * v = j
 * ^ = k
 */
int getchoice()
{
    int c;
    char s[20];
    
    if (fgets(s,20,stdin)){
        c=s[0];
        switch (c){
            case 0x1b: /* ESC */
                if (s[1] == 0x5b){
                    /* arrow keys are pressed */
                    switch (s[2]){
                        case 0x41: /*up arrow*/
                            c='k';
                            break;
                        case 0x42: /*down arrow*/
                            c='j';
                            break;
                        case 0x44: /*l arrow*/
                            c='h';
                            break;
                        case 0x43: /*r arrow*/
                            c='l';
                            break;
                        default:
                            c=0;
                    }
                }else{
                    c=0;
                }
                break;
            case ' ':
            case 'h':
            case 'j':
            case 'k':
            case 'l':
            case 'q':
            case 'x':
                break;
            default:
                c=0;
        }
        return(c);
    }
    return(0);
}
    
/* Set the Terminal to Non Canonical mode with echo off
 * or reset the terminal.
 * USAGE: set_terminal(1) for canonical
 */
int set_terminal(int canonical)
{
    static struct termios originalsettings;
    struct termios newsettings;
    static int origok=0; /* set if originalsettings valid */
    if (canonical){
        /* save original settings and set canonical mode*/
        tcgetattr(fileno(stdin),&originalsettings);
        newsettings=originalsettings;
        newsettings.c_lflag &= ~ICANON;
        newsettings.c_lflag &= ~ECHO;
        newsettings.c_cc[VMIN]=0; /* do not block */
        newsettings.c_cc[VTIME]=1; /* 100 ms */
        if (tcsetattr(fileno(stdin),TCSANOW,&newsettings) !=0){
            die("ERROR: could not set terminal attributes on stdin\n");
        }
        origok=1;
    }else{
        if (origok){
            /* restore settings */
            tcsetattr(fileno(stdin),TCSANOW,&originalsettings);
        }
    }
    return(0);
}

/* open /dev/parportX device and claim it.
 * USAGE: fd=robi_claim("/dev/parport0");
 * The return value is a file descriptor used by other
 * functions such as robi_setdata */
int robi_claim(char *dev)
{
    int fd,i;

    fd = open(dev, O_RDWR );
        if (fd < 0) {
                die("ERROR: cannot open device %s\n",dev);
        }
    i=0;
    /* we need exclusive rights as we do not set the control lines*/
    /*ioctl(fd, PPEXCL, &i)&&
           die("ERROR: request for exclusive rights failed\n");*/
    ioctl(fd, PPCLAIM, &i)&&die("ERROR: could not claim parport\n");
    return(fd);

}
/* Walk left
 */
int walkleft(int fd)
{
    /* first B legs to ground */
    robi_setdata(fd,LEGBD);
    usleep(400 *1000);
    /* all A legs 1 step */
    robi_setdata(fd, LEGB1 | LEGB3 );
    usleep(1100 *1000);

    /* first A legs to ground, cool B*/
    robi_setdata(fd,LEGAD);
    usleep(400 *1000);
    robi_setdata(fd,0);
    usleep(1000 *1000);
    return(0);
}
/* Walk right
 */
int walkright(int fd)
{
    
    /* first A legs to ground */
    robi_setdata(fd,LEGAD);
    usleep(500 *1000);
    robi_setdata(fd,  LEGA3 | LEGAD);
    usleep(300 *1000);
    /* all A legs 1 step */
    robi_setdata(fd, LEGA1 | LEGA3 );
    usleep(1100 *1000);

    /* first B legs to ground, cool A*/
    robi_setdata(fd,LEGBD);
    usleep(400 *1000);
    robi_setdata(fd,0);
    usleep(1000 *1000);
    return(0);
}
/* Walk with all 3 legs 1 step forward
 */
int walkstraight_a(int fd)
{
    
    /* first A legs to ground */
    robi_setdata(fd,LEGAD);
    usleep(800 *1000);
    /* all A legs 1 step */
    robi_setdata(fd, LEGA1 | LEGA2 | LEGA3 );
    usleep(1000 *1000);

    /* first B legs to ground, cool A*/
    robi_setdata(fd,LEGBD);
    usleep(500 *1000);
    robi_setdata(fd,0);
    usleep(1200 *1000);
    return(0);
}
/* Walk with all 3 legs 1 step forward
 */
int walkstraight_b(int fd)
{
    /* first B legs to ground */
    robi_setdata(fd,LEGBD);
    usleep(400 *1000);
    /* all B legs 1 step */
    robi_setdata(fd,LEGB1 | LEGB2 | LEGB3);
    usleep(1000 *1000);
    /* A down and cool */
    robi_setdata(fd,LEGAD);
    usleep(800 *1000);
    robi_setdata(fd,0);
    usleep(1200 *1000);
    return(0);
}
/* Walk with all 6 legs 1 step back
 */
int walkback(int fd)
{
    
    /* first A legs to ground */
    robi_setdata(fd,LEGAD);
    usleep(800 *1000);
    /* all B legs 1 step in the air*/
    robi_setdata(fd, LEGB1 | LEGB2 | LEGB3 );
    usleep(500 *1000);

    /* first B legs to ground, cool A*/
    robi_setdata(fd,LEGBD);
    usleep(500 *1000);
    /* all A legs 1 step in the air*/
    robi_setdata(fd,LEGA1 | LEGA2 | LEGA3);
    usleep(500 *1000);
    /* A down and cool */
    robi_setdata(fd,LEGAD);
    usleep(800 *1000);
    robi_setdata(fd,0);
    usleep(1000 *1000);
    return(0);
}
/*---------*/
/* Write a bit pattern to the data lines
 * USAGE: rc=robi_setdata(fd,bitpat);
 * The return value is 0 on success.
 */
int robi_setdata(int fd,unsigned char bitpat)
{
    int rc;

    rc=ioctl(fd, PPWDATA, &bitpat);
    return(rc);
}

==== robi.h =====
/* vim: set sw=8 ts=8 si et: */
#ifndef H_ROBI
#define H_ROBI 1
#define VERINFO "version 0.2"


/* the first thing you need to do: */
extern int robi_claim(char *dev);

/* write a bit pattern to the data lines of the parallel port: */
extern int robi_setdata(int fd,unsigned char bitpat);

/* input and terminal functions */
extern int set_terminal(int canonical);
extern int getchoice();
extern int walkstraight_a(int fd);
extern int walkstraight_b(int fd);
extern int walkback(int fd);
extern int walkleft(int fd);
extern int walkright(int fd);

/* data pins to legs: 
 * A1------=------B1
 *         =      
 *         =      
 * B2------=------A2
 *         =      
 *         =      
 * A3------=------B3
 *
 *
 * Pin to set A-legs to ground= AD
 * Pin to set B-legs to ground= BD
 *
 * parallel port    leg name
 * -------------------------
 * data 0           A1
 * data 1           A2
 * data 2           A3
 * data 3           AD
 * data 4           B1
 * data 5           B2
 * data 6           B3
 * data 7           BD
 */
#define LEGA1 1
#define LEGA2 2
#define LEGA3 4
#define LEGAD 8
#define LEGB1 16
#define LEGB2 32
#define LEGB3 64
#define LEGBD 128

#endif


The software uses the ppdev programming interface from the 2.4.x Kernel (You need a 2.3.x or 2.4.x Kernel. It will not work with older kernels). This is a clean and convenient interface for writing user space parallel port device drivers. In older kernels we would have had to write a kernel module or use a rather ugly method which would only allow the user root to run the program. The ppdev interface uses the device file /dev/parport0 and by adjusting the owner and permissions of that file you can control who is allowed to use this parallel port interface.

To compile the ppdev as a module into your kernel you need to compile the PARPORT module together with the PPDEV device. This should then look as follows in the .config file:

#
# Parallel port support
#
CONFIG_PARPORT=m
CONFIG_PARPORT_PC=m
CONFIG_PARPORT_PC_FIFO=y
# CONFIG_PARPORT_PC_SUPERIO is not set
# CONFIG_PARPORT_AMIGA is not set
# CONFIG_PARPORT_MFC3 is not set
# CONFIG_PARPORT_ATARI is not set
# CONFIG_PARPORT_SUNBPP is not set
CONFIG_PARPORT_OTHER=y
CONFIG_PARPORT_1284=y
#
# Character devices
#
CONFIG_PPDEV=m
#


The program first claims (initializes) the parallel port with the ioctl command PPCLAIM. Then it sets the terminal to non canonical mode. This is to get the input directly from the keyboard without the user always having to press return after each input. Next it goes into a loop where it first checks if there was any user input and then lets the robot walk according to the command. If you don't do anything the program will just continue with the last command (e.g continue to walk straight).
The command ioctl(fd, PPWDATA, &bitpat); is used to set the data lines to a given bit pattern.

The pins from your robot need to be connected to the output lines of the driver circuit as follows:

Legs:
  A1------=------B1
          =      
          =      
  B2------=------A2
          =      
          =      
  A3------=------B3
 
 
  Pin to set A-legs to ground= AD
  Pin to set B-legs to ground= BD

Corresponding output lines of the driver circuit:
  data 0           A1
  data 1           A2
  data 2           A3
  data 3           AD
  data 4           B1
  data 5           B2
  data 6           B3
  data 7           BD

Data 0 is the output of the driver circuit that connects to the parallel port at pin 2 (D0).

Here it is, the walking robot:
[yes, it walks]
It moves a bit too fast on this animated gif. In reality it is a bit slower due to the cooling times that you need to have for the nitinol to get back to its original length.


We hope that you had a lot of fun building the robot. Just let us know about your robot, especially if yours is built with a different design!

References

mirror server hosted at Truenetwork, Russian Federation.