HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML

Back when Java was called Oak, it was thought that this new language would be ideal for developing embedded applications, such as those that would run on set-top boxes. The developers of this new language were well ahead of their time. Java's momentum began to build not from its large set-top developer community but from developers wishing to enhance their Web sites using Java applets. Thank goodness that was short-lived!

Java then became popular in server-side applications, but it has only recently begun to gain popularity in the embedded devices it was originally intended for. Java is now running on, and in, everything from big SMP servers to portable devices, such as PDAs, phones, and even smart cards.

Like many Java enthusiasts, I'm interested in exploring the capabilities of Java programs on small-footprint devices, especially my own Palm Pilot. Sometimes the best way to learn more about a new technology, or to develop a new skill, is to just mess around with stuff. Some refer to this as hacking (not to be confused with cracking, which is breaking into computer systems illegally).

Not too long ago I discovered plans for a Palm Pilot Robot at Carnegie Mellon University's School of Computer Science. I downloaded the plans, bought all the necessary parts (except for the Palm Pilot, which I already had), and started putting it together. After I got the robot assembled and sandwiched together, I immediately wanted to see if I could control it with a Java program.

I learned a great deal while hacking a few Java programs to control various parts of the Palm Pilot Robot Kit (PPRK) hardware, and much of what I learned can be applied directly to embedded applications.

For example, I decided to construct a simple framework that attempts to make programming robot software, for robots similar to the PPRK, much easier.

This article discusses the lessons learned in writing Java software to make this robot come alive - well, sort of alive.

Java programs running on your favorite handheld device can serve as the brains for your robot creation.

Waba
Today there are several Java, or Java-like, Virtual Machines (VMs) for running embedded Java applications. Waba is one such VM. The Waba SDK provides a Java-like development environment for small-footprint devices and is available under the terms of the GNU Public License (GPL). Because it's an open source VM implementation, a large group of developers continue to enhance and improve the Waba API on a project called SuperWaba.

I didn't do extensive research on what VM I wanted to use and run on my PPRK. I had some experience with Waba already and have found it easy to understand, download, and install. The fact that it's distributed under the GPL is also in its favor. Furthermore, the SuperWaba newsgroup is very active - and developers, such as Guihlerme Campos Hazan (lead SuperWaba developer), are friendly and quick to answer newbie developer's questions. These types of development communities make it easier to get up to speed on new and emerging technologies.

After assembling my PPRK (see Resources section) and choosing a VM for my Palm V that could run Java-like programs, I was ready to start writing some Java code.

It's All About the Serial Port
The key to writing a Waba application to control your Palm Pilot Robot is a good understanding of the following:

  1. The protocol the Pontech SV203 servo controller board "speaks"
  2. How to use the Waba SerialPort class
The Palm Pilot Robot, designed by Grigoriy Reshko at Carnegie Mellon University, uses the Pontech SV203 servo motor controller board to control the three onboard servo motors and to gather data from the three infrared sensors located by each motor. The communication protocol of the Pontech SV203 servo motor controller board is straightforward. When building a PPRK from scratch, you must modify one of your extra HostSync cradles by lopping off the end of the cable and fitting it with a male DB-9 connector. This connector plugs into the female end of the SV203 board. Thus, your modified HostSync cradle is the means by which your Palm device can communicate with the SV203 board.

As an aside, if you decide to buy the PPRK kit from Acroname, which I recommend, this modified HotSync cable is provided for you in the kit. (Note: You can download a PDF version of the SV203 manual from www.pontech.com to learn more about the SV203 design and protocol specifications.)

Understanding how to first send commands from your PC to your SV203 board using a serial cable (at your local Radio Shack) and a terminal emulation program is important. I have documented some notes and procedures on how to do this, as well as notes for debugging your PPRK hardware and software, at www.trexlabs.com/pprk.

Communicating with the SV203 is quite simple. You need to tell the board what servo motor you wish to control and in what direction and speed you wish to move it. You do this by generating a string of ASCII characters that represent a command and the device number you wish to control, terminating the string with a "\r".

For example, the command SV1 M55 turns servo 1 counterclockwise (counterclockwise movement is any number between 0 and 127, and clockwise movement is any number between 128 and 255. Also note that the motors turn slower the farther away from 0 up to 127 you get, and faster the farther from 128 up to 255 you get).

Once you establish a connection between your SV203 and your PC's terminal emulation program (on Linux I use minicom), you can type the command above, hit return (enter), and servo motor 1 should begin a clockwise rotation. To stop the motor, enter the command SV1 M0 and hit return (enter).

The PPRK has three motors, so you can control all three by making the appropriate substitutions in the command sent to the SV203 board. Now all you need to figure out is how to write these same commands to the serial port in our Waba program.

Waba makes this easy to do. As mentioned earlier, Waba has a class in the waba.io package called SerialPort, which you'll use to open a connection to the SV203 controller board serial port. This class can be constructed in two ways:

SerialPort(int port_number, int baud_rate)
SerialPort(int port_number, int baud_rate,
int bits, boolean parity, int stopbits)
The first constructor constructs the SerialPort object using 8 bits, no parity, and 1 stop bit as the default, so you can use the following to establish a serial connection with the SV203 controller board:

SerialPort sp = new SerialPort(0, 9600);
sp.setFlowControl(false);
Now, you need to populate an array of bytes containing the appropriate commands, and then write that array to the serial port. If you want to write the commands SV1M55, you can accomplish it like so:

byte[] buff = new byte[7];
buff[0] = (byte)'S';
buff[1] = (byte)'V';
buff[2] = 49;
// ASCII equivalent of 1.
Very important!
buff[3] = (byte)'M';
buff[4] = 53;
// ASCII equivalent of 5.
buff[5] = 53;
buff[6] = (byte)'\r';
// End of command
Now you're ready to write this array of bytes to the serial port object instance, sp, which was created earlier. The following code snippet uses our SerialPort instance to write the 7- byte array that contains the command to the Palm's serial port and out to the SV203 board:

sp.writeBytes(buff, 0, 7)
After your Waba application has sent this command in the manner described previously, your servo motor 1 on your PPRK should begin spinning lazily along in a counterclockwise direction. Again, you can stop it by issuing the SV1 M0 command in a similar manner.

Give the Gift of Sight
The PPRK uses three Sharp GP2D12 Infrared Object Detectors, which are also connected to your SV203 board. These little infrared sensors are used to detect nearby objects. The GP2D12 IR sensor will change the voltage on a given SV203 port, based on the distance, in centimeters, a given object is from the sensor. You communicate with these sensors the same way you communicate with the servos, except this time you must also read a 4-byte response from the board. The SV203 protocol for sending a request to the IR sensor is simply to send the following ASCII command: AD1\r. This tells the SV203 board to take a reading from the first IR sensor and to send 4 bytes back to our Palm device indicating the range, in centimeters, of the closest object, if any. You can create a 4-byte array and populate it with the command like so:

byte buff[] = new byte[4];
buff[0] = (byte)'A';
buff[1] = (byte)'D';
buff[2] = 49; // ASCII equivalent of 1.
buff[3] = (byte)'\r';
You then send the 4-byte command using the SerialPort instance created earlier. You need to be aware of one subtle nuance before doing this. Your Waba program can write and read to the serial port faster than the information can be processed by the SV203 board, so you need to give the board time to digest its contents when reading data back from the board. The SV203 User's Manual suggests a sleep time of approximately 3 milliseconds, but give it a little more just to be sure. You can do this with Waba using the waba.sys.Vm.sleep() static method. The parameters to this method indicate the number of milliseconds the VM should sleep.

sp.writeBytes(buff, 0, 4);
waba.sys.Vm.sleep(15);
You should expect the SV203 board to send some information back at this point, hopefully in the form of 4 bytes. These 4 bytes will contain a single-digit, two-digit, or three-digit number followed by a \r. Note: The largest value you should get back from the SV203 is 255 and the smallest is 0. Here's one way you can read the results and set the value in a waba.ui.Label object:

Label lblIRVal = new Label("");
sp.readBytes(buff, 0, 4);
StringBuffer sbsensorin = new StringBuffer();
for (int i=0;i<4; i++) {
if ( ( buff[i] != 0x0 ) && ( buff[i]
!= (byte)13 ) && (buff[i] != (byte)10 ) )
sbsensorin.append( (char) buff[i] );
}
lblIRVal.setText( sbsensorin.toString());
The Waba Open Robot Framework (WORF)
It's a useful exercise to take the code written thus far to create an API Framework that would allow other hackers to quickly and easily develop software for their own robot creations. The idea behind the Waba Open Robot Framework is to create Java components for each major hardware component composing the PPRK, while at the same time trying to keep it flexible and extensible enough to accommodate other robot configurations. WORF is an open source project and is governed under the terms of the LGPL. You can grab a snapshot of WORF from http://worf.sourceforge.net.

One of the truly neat things about Waba is the availability of the Waba VM on several different embedded platforms, such as Palm, iPaq, Apple's Newton, and even DOS. While I have not tested the portability of WORF across these platforms, it's certainly conceivable that robots can be built using any of these devices and powered with Waba programs built on top of WORF.

The PPRK consists of three servo motors, three GP2D12 infrared sensors, and a Pontech SV203 Controller Board to coordinate and control these devices. WORF, therefore, consists of Java components for each, and provides a burgeoning API set for easily sending messages to the SV203 board on behalf of a given component. The utils package provides miscellaneous support functions (see Figure 1).

Figure 1
Figure  1:

With WORF, developers of embedded robot software applications can focus more on the creative aspects of what they want their robot to do and less on the details of how to communicate with this specific type of hardware. This is accomplished by encapsulating the details of sending the specific commands described earlier to the SV203 board and making this functionality available vis-á-vis an easy-to-use set of APIs.

SV203 Component
The SV203 Waba component extends, or subclasses, the waba.io.SerialPort class and provides three constructors, each of which provides the developer with a different means of initializing the parent SerialPort class. The SV203 Waba component maps to the Pontech SV203 controller board piece of hardware. The best reason to morph the waba.io.SerialPort class into the SV203 class, rather than just use the SerialPort object provided by Waba, was the need to wait at least 3 milliseconds for the SV203 board to process commands that require a response. While this sleep could just as easily have been put in the IRSensor class, there was also the aesthetic reason to have an SV203 Waba component that could easily be paired with the SV203 hardware component. Using this Waba component as a fundamental building block of the other two, the servo and IR components can easily wrap convenient API calls around the lower-level serial port writes and reads employed by the SV203 Waba component.

Servo Component
The PPRKServo component takes an instance of the SV203 object as an argument to its constructor. As a result, the PPRK Servo component implements easy-to-use APIs, like turnRight, turnLeft, stop, moveServo, and moveHolonomic. These APIs, in turn, stuff a byte array with the appropriate ASCII commands and call the appropriate write method on the SV203 instance.

IR Component
The IRSensor component also takes an instance of SV203 as an argument to its single constructor. As stated earlier, when communicating with the GP2D12, a response is expected from the SV203 controller board with each command sent to it requesting object location information. The IRSensor component implements methods to poll a particular IR sensor for a data reading and also includes methods getStringPortValue and getIntPortValue, which return the IR sensor readings as either an int or a string.

Utilities
Finally, WORF currently uses a simple class, Utils, to provide static functions for byte-to-ASCII conversions, and vice versa. It's meant to be a catch-all class for methods that don't really fit anywhere else.

Putting It All Together
Okay, so here you are with our PPRK hardware all assembled and functioning (see Resources for WORF diagnostic utilities and hardware troubleshooting tips), and you have a basic knowledge of how to develop for the robot. But what can you do with it?

Well, at the risk of using a cliché, you're truly limited only by your imagination, creativity, and attention span to your project.

My first idea was an attempt to make my PPRK do a Mexican Hat Dance by writing a short program using WORF. Listing 1 is the complete source code for my WORF Mexican Hat Dance application, which you can run on your own PPRK (see Figure 2).

Figure 2
Figure  2:

This is a simple demonstration of how easy it is to write a small robot program for your PPRK using WORF. The WORFHatDance application (see Resources) causes the robot to alternately turn right and left at different stages of the musical ditty it plays when you tap the Push Me button displayed on your Palm (see Figure 3). The static waba.fx.Sound.tone() method tells your Waba application to play a tone at the given frequency for the given duration. The tones specified in Listing 1 play some semblance of the Mexican Hat Dance, even if it is a little off key at times.

Figure 3
Figure  3:

I'm sure many reading this article are asking themselves, "What's the real value in building a PPRK and writing software for it? Could I enter my PPRK in the BattleBots competition? More important, how can I make money with it?" The real value in these types of exercises, however, is fun and education. Granted, the PPRK, as it is, probably couldn't easily be modified to do your laundry or sweep your floor, and wouldn't last two seconds in the BattleBots arena. The servo motors aren't strong and the GP2D12 IR sensors are somewhat delicate.

But I have to admit to a certain childlike fascination in writing a few lines of code capable of controlling hardware external to my Palm device. The PPRK provides a good platform for investigating possibilities in controlling and interacting with robotic hardware from a small footprint device, such as a Palm or iPaq. As far as I'm concerned, building robots and controlling them with my Java programs is just good fun!

The money will come, hopefully, from the knowledge you gain from basic projects such as this, strengthening your skills in embedded device programming, and contributing code - and lessons you've learned - to the Java development community at large.

Resources

  1. Informal notes on building and troubleshooting the PPRK: www.trexlabs.com/pprk
  2. Official Carnegie Mellon University PPRK: www.cs.cmu.edu/~pprk
  3. The Waba VM: www.wabasoft.com
  4. The SuperWaba VM: www.superwaba.org
  5. Waba Newsgroup: news: news://news.falch.net/pilot.programmer.waba
  6. Source repository for WORF, the WORF Diagnostic Utility, and the WORFHatDance program: http://worf.sourceforge.net

Author Bio
James Caple is an independent programmer and author. He has more than eight years of industry experience, including building mobile device synchronization software and systems in Java. He is a Sun Java 2 certified programmer and developer. [email protected]

	



Listing 1

package com.trexlabs.worf.robot;


import com.trexlabs.worf.sv203.*;
import com.trexlabs.worf.ir.*;
import com.trexlabs.worf.servo.*;


import waba.ui.*;
import waba.fx.*;
import waba.io.*;


/**
 * An example program using WORF to make your robot dance while it
 * plays a catchy tune.
 *
 * @author James Caple Copyright (C) 2001, All Rights Reserved
 */


public class WORFHatDance extends MainWindow {


    private Button begin;
    private SV203 board;
    private PPRKServo servos;


    public WORFHatDance() {
        begin = new Button("Let's Dance!");
        begin.setRect(50, 85, 59, 20);
        add(begin);


        // WORF Setup
        board = new SV203();
        servos = new PPRKServo(board);
    }


    /**
     * Draw about Strings.
     * @param Graphics
     * @return void
     */
    public void onPaint( Graphics g ) {
        g.setColor(0, 0, 0);
        g.drawText("WORF Hat Dance Example", 0, 0);
        g.drawText("Copyright (C) 2001, James Caple", 0, 10);
        g.drawText("http://worf.sourceforge.net", 0, 20);
    }



    public void onEvent(Event evt) {
        if (evt.type == ControlEvent.PRESSED && evt.target == begin) {
            getYourGrooveOn();
        }
    }


    private void getYourGrooveOn() {
        // Swing to your right
        servos.turnRight((byte)3);


        Sound.tone(3000, 500);
        Sound.tone(2900, 200);
        Sound.tone(3000, 200);
        Sound.tone(2600, 200);
        Sound.tone(2500, 200);
        Sound.tone(2600, 200);
        Sound.tone(2100, 200);
        Sound.tone(2000, 200);
        Sound.tone(2100, 200);
        Sound.tone(1500, 500);


        // Swing to your left
        servos.turnLeft((byte)3);


        Sound.tone(1300, 200);
        Sound.tone(1400, 200);
        Sound.tone(1500, 200);
        Sound.tone(1700, 200);
        Sound.tone(1900, 200);
        Sound.tone(2000, 200);
        Sound.tone(2300, 200);
        Sound.tone(2500, 200);
        Sound.tone(2700, 200);
        Sound.tone(2300, 500);


        // Swing to your right
        servos.turnRight((byte)3);


        Sound.tone(2700, 200);
        Sound.tone(2600, 200);
        Sound.tone(2700, 200);
        Sound.tone(2300, 200);
        Sound.tone(2200, 200);
        Sound.tone(2300, 200);
        Sound.tone(1900, 200);
        Sound.tone(1800, 200);
        Sound.tone(1900, 200);
        Sound.tone(1500, 500);


        // Swing to your right
        servos.turnLeft((byte)3);


        Sound.tone(3000, 200);
        Sound.tone(3000, 200);
        Sound.tone(3000, 200);
        Sound.tone(3300, 200);
        Sound.tone(3000, 200);
        Sound.tone(2700, 200);
        Sound.tone(2500, 200);
        Sound.tone(2200, 200);
        Sound.tone(2000, 500);


        Sound.tone(4000, 100);


        // Cha cha cha!
        servos.stop();
        servos.turnRight((byte)3);
        servos.turnLeft((byte)3);
        servos.turnRight((byte)3);
        servos.stop();
    }


}

  
 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.