|ince the dawn of recorded history, humans have engaged in a relentless search for ever geekier applications of technology. As a firm proponent of this societal trend, I've recently embraced one of its best examples—global positioning satellite (GPS) technology. I wrote a small program in Visual Basic® that collects readings from a GPS device and displays the information on my laptop. Before long, I was driving around in my car, staring at my laptop screen as it collected my exact latitude and longitude. Then I came up with a great idea: what if I stopped for lunch?|
While I was enjoying my sandwich, another thought hit me: what if I took this program off my laptop and migrated it to my Pocket PC? It makes a lot more sense to carry one of these babies around when you're in the woods. A handheld device is about eight pounds lighter than a fully loaded laptop, and the batteries last many times longer. In this article, I'll tell you exactly how I got the program working on the Pocket PC.
The program I'll describe is unusual in that you need a GPS device and a Pocket PC to use it. There are several suitable GPS devices on the market. I found an older Delorme TripMate in my office one day, and I used it for the development of this program. Unlike many other modern peripherals, you don't get a big speed boost by selecting the newest version of a GPS device—the standard serial output is 4800bps, and this program polls for input every two seconds. To connect the GPS device to my Pocket PC (an HP Jornada 545), I also needed to invest in a CF+ serial card—more on this later. First, let's go through a bit of background to explain why this is not as difficult a project as it sounds.
GPS Basics GPS receivers have been around for several years now. They continually search for input from a series of fixed-orbit satellites, each of which transmits its position, the current time, and so on. The GPS receiver locks onto as many of these signals as it can, then calculates its current position on earth from the information returned by the satellites.
The information that a GPS receiver returns is standardized, based on specifications published in the National Marine Electronics Association (NMEA) 0183 protocol. Just about any GPS device will transmit a series of informational strings at 4800bps, although you need to first send the device an initialization string that contains information like how often you want to receive transmissions, your approximate current position (to help the device lock onto satellites more quickly), and so on. I'll discuss this in more detail later. Some devices will transmit more of these strings than others, depending on what the device was designed to do.
An example of a transmitted string is:
This string starts with $GP (which stands for global positioning), then adds a three-character code, GGA, which means that the string will contain global positioning fix data. In this case, the information is being sent at 20:01:36 UTC. The GPS device is reporting that it's fixed at 40.432808 N latitude, 73.174976 W longitude. The 1 means that this information is valid (and not just carried over from a previous run). The device is tracking 3 (03) satellites. The horizontal dilution of position is 1.27, the device is about 4.5 meters above sea level, the mean sea level is 34.4 meters below the WGS84 ellipsoid, and there are two pieces of DGPS information at the end. (The WGS84 is the World Geodetic System 1984 ellipsoid, an imaginary globe-shaped coordinate system that most closely matches the shape and position of the earth itself, and is used as a reference for GPS data.) The string ends with a *41, which means that its checksum is 41.
There are only a few string definitions like this you need to be aware of when writing a simple GPS reader, and they're documented in Figure 1. The Delorme TripMate device I used to collect this information is three years old. The batteries last about 10 hours (roughly the same as my Jornada's batteries last), and the unit turns itself on and off when it senses that it's been attached to a standard 9-pin serial connection. You just have to aim it out a window (or better, use it outdoors) until you get at least three satellites fixed—the minimum for a reliable positional reading.
Writing the Program using Visual Basic Since I had never tried anything like this before, I started from scratch—by writing a program that just collected input from the serial port. Since you can't effectively poll a port directly from Visual Basic, I used the Microsoft® Comm (MSComm) control in combination with the Timer control to write a simple program that polls the serial port once a second and writes any input to a textbox. The only special trick I used here (if you can even call it a trick) was parsing the input into phrases delimited by end-of-line characters, indicated by the vbCrLf constant. The code for this CommCheck program is shown in Figure 2; the program itself is shown in Figure 3.
Figure 3 CommCheck in Action
So far, so good. I used CommCheck to determine the proper comm port settings for the GPS device, to get flow control right, and to build the general input collection routine. Once this was all worked out properly, I set about building the actual GPS reader. One great thing about GPS is that there are so many things you can do with just a little incoming data. I decided that I wanted the program—at least the desktop version—to have the following seven features:
As you can see from the output string definitions in Figure 1, you get most of the information you need—position, speed, bearing, and altitude—without doing anything more than parsing data. The code for my GPS reader, named GPSBoy, simply eats through the incoming data, parses it into individual lines of EOF-delimited data, and sends each line to an appropriate subroutine for further comma-delimited data parsing. This is a handy design because you can ignore particular sentence types until you're ready to deal with it. (If no parse routine exists for a sentence, the switch statement just ignores it.)
- Optional save-to-disk of incoming data
- Emulation mode, so I could use existing data for testing
- Display of current time, latitude, longitude, altitude, and speed
- Graphical display of current directional heading
- List of the satellites the receiver could see
- Graphical representation of where the satellites are in the sky
- A way to see a map of the current location
When you first plug in a GPS device, it needs to hear from you before it starts to send data. In particular, you can give it the coordinates of the location you think you're in. The closer your input is to your real location, the less time the device will take in order to locate satellites in the sky. The initialization strings that my TripMate uses are described in Figure 4.
When you click Go, GPSBoy builds an initialization string and sends it to the device. If you don't do this, the TripMate will output a power-on ID string ($PRWIRID), then will spew out the string "ASTRAL" repeatedly until it's either initialized or it eats through its four AA alkaline batteries. After it receives an initialization phrase, it begins to emit one of a number of phrases that you saw in Figure 1. The output phrases get much more interesting when the device is actually locked onto some satellites; you begin to find real information like current speed and heading in the strings.
Instead of toying around with the annoying file functions in Visual Basic, I used the FileSystemObject for the save-to-disk and emulation features. If you haven't used these yet (or even if you have), they're pretty cool. I just create an object when the program starts up:
When I want to open a file for writing, I just pop up a common dialog control (while you weren't looking, I put one on my form and named it CmnDlg) and open the file the user selects:
Dim fs as Object
Set fs = _ CreateObject("Scripting.FileSystemObject")
FOut is an object representing a file. I can say FOut.WriteLine to put a string into the file, b$ = FOut.ReadLine to input a line from the file, or FOut.Close to close the file. (Note that all input and output operations are dependent on the mode in which you opened the file. For example, you can't write to the file if it's opened as read-only.) It's a lot easier than looking for the next free file number and trying to control the intrinsic Visual Basic file handlers.
CmnDlg.FileName = "gpsout.txt"
Set FOut = fs.CreateTextFile(CmnDlg.FileName, 2) ' open for writing
Since I wanted to plot satellites on a grid, I actually had to turn my brain on to remember how to convert polar coordinates to Cartesian (x,y) equivalents. The location of a satellite is expressed in altitude (degrees up in the sky) and azimuth (degrees from due north), and the Visual Basic picturebox control prefers x,y coordinates when you use its Line, Circle, and Point methods. It's just fussy that way.
One thing you discover after a few years out of high school is that you don't remember what the heck sine and cosine are used for. (Or am I just alone in this? Please, no wagering.) Another thing you discover is that Visual Basic doesn't have a defined constant for pi, and the existence of said constant would make circle-based measurements somewhat easier. After hanging my head in shame and looking up all the proper formulas online, I determined that a function like the one in Figure 5 would convert from an elevation, azimuth measurement to its x,y equivalent. Note the little trick I did with the azimuth, shifting it 90 degrees so that north would appear to be at the top of the graph.
I also wanted to cheat a bit for the map display. It's not feasible to ship this product with a CD full of map data, so when a map is requested I bring up a new window with a WebBrowser control that navigates to a mapping page on the U.S. Census Tiger site and use the current latitude and longitude (which I save in a global variable) as arguments.
Of course, this only works when you're online, so don't try it in the car if you don't have wireless Internet services.
curURL = "http://tiger.census.gov/cgi-bin/mapgen?&mlat=" & _
curlat & "&mlon=" & curlng & "&msym=bigdot&lat=" & curlat & _
"&lon=" & curlng & "&wid=1.000&ht=1.000"
frmMap.Caption = "Show map (" & curlat & "," & curlng & ")"
Figure 6 GPSBoy
Okay, so maybe GPSBoy isn't the world's most robust program, but I think it has a certain cheesy charm to it. And it's really cool when that first data starts streaming down and makes the thing work (see Figure 6). If it doesn't work, make sure you have the MSComm control's CommPort setting pointed to the proper port—mine is on COM1, so I set this to 1. (No dialog to select the comm port? Pretty cheesy.) The complete code for GPSBoy is available from the link at the top of this article.
Porting to eMbedded Visual Basic So the program works, but who wants to carry around a laptop on their dashboard just to get GPS features? This type of application is ideal for a compact, handheld device. At first, I thought it would be a pretty easy port from Visual Basic 6.0 to eMbedded Visual Basic, but nothing's ever that simple.
In retrospect, I should have read the eMbedded Visual Basic help file. After warning that porting a desktop program from Visual Basic to eMbedded Visual Basic "presents some challenges" but that "some code may be reusable," it lists several places where you'll probably have to scrap at least some portion of your work:
Give up hope? Hardly. Porting a desktop application in Visual Basic to the Pocket PC isn't all that bad once you get a feel for it. The biggest mental roadblock is getting over the urge to do clever things that aren't fully supported on the Windows® CE platform. You also need to be prepared to clean up your UI and feature list. Remember, your program should be a slimmed-down version that contains the functionality that makes the most sense in a mobile application. In my GPS program, I wanted to keep at least the following features: reading data from the comm port; reading data from a file (for testing purposes); and positional, directional, and speed display.
- A complete redesign of the UI may be necessary to facilitate usability on a smaller screen.
- Many of the controls available to the desktop version of Visual Basic are not supported in eMbedded Visual Basic. Some controls have been ported to eMbedded Visual Basic with reduced functionality.
- Only the Variant data type is supported in eMbedded Visual Basic; User Defined Types (UDTs) are not supported. The eMbedded Visual Basic language is based on VBScript, and thus has the same limitations inherent in that language.
Since the Windows CE platform was completely new to me, I thought I'd do things step by step, and test simple stuff like comm input before attempting to port the entire program. I just opened up the CommCheck project in eMbedded Visual Basic, compiled it, and it ran great. Wrong! A project created in Visual Basic isn't directly importable into eMbedded Visual Basic. In most cases, you can't even open a form in this manner because standard controls generally have different versions for desktop Windows and Windows CE. The MSComm control, for instance, must be added to a form as the Microsoft CE Comm Control 3.0, not the Microsoft Comm control. There's no way to get around this—if you try to go ahead and load the form anyway, you'll have to replace all the references in your form because the GUIDs of the components won't match.
So I went through the tedious chore of recreating the four controls on the CommCheck project. After two grueling minutes, I had given all the new controls the same names that they had in the desktop version, imported the event code, and tested the program. In eMbedded Visual Basic, you have the handy option of running your debugging session either on a spiffy little emulation program (see Figure 7) or on a connected device. Since I was doing some comm input, and the desktop emulation couldn't reach the local comm port, I went straight to the device.
Figure 7 GPSBoy/CE Emulator
Comm input should be easy—the device even comes with a serial cable so you can use ActiveSync®, right? So I just plugged in the serial sync cable, connected the GPS to a gender changer, then to the cable (both were 9-pin female connectors, because they were both meant to go to the 9-pin male input on a PC). And presto! Nothing happened. I added a null modem, no easy feat because my local computer store had never heard of the 9-pin model. The sight of a device connected to a 9-to-25-pin converter then to a 25-pin null modem, then to a 25-to-9-pin converter, then to a gender changer may have been a great comedy situation, but it didn't actually provide any serial input. I e-mailed Hewlett-Packard technical support as well as a Microsoft contact, and they both told me that I would need to buy a CF+ serial card and then plug it into the Jornada's expansion slot.
I installed the serial card and plugged the GPS directly into it. Still nothing. Then I remembered to fix the input port setting. The CF+ serial card is recognized as COM2 by my Pocket PC. Finally, this time, I hit paydirt! GPS information started to stream in just as I'd expected.
The next step was to optimize the user interface for the smaller Pocket PC screen. Pocket PC's resolution is 240x320 (1/4 of standard VGA, turned horizontally). Programmers tend to treat screen real estate like they do memory and hard drive space—it's there to be used up. On the contrary, you have to make some real design trade-offs when targeting a smaller device. Instead of jamming every last piece of information onto a single screen, redesigning for the smaller Pocket PC screen quickly clarifies what's actually important to the program.
Out went the line-by-line input display. Down went the size of some of the display elements. Out went extra buttons, as well as the map feature. (I figured that if you have this program out in the wild, you probably weren't connected at the time. Maybe in 18 months I'll revisit it.) All the settings went into their own dialog.
Besides the obvious memory constraints to consider when targeting a machine that generally has 16MB RAM, there's also a code cleanup issue. When you display a form in a Windows CE-based program, that form stays up in its last-known state until the program itself is ended. This can be a boon, since you don't have to read and update settings just before you display a box like this. However, if the window lets you change any program-wide settings, you have to make sure to save them when the OK or Apply button is pressed—not when you exit the form. You also need to hide the form when the user clicks OK, and provide some sort of a Reset or Revert button so that the user can dump their changes, since they can't just cancel out of the form without processing.
The Program Itself My GPS program for the Pocket PC, GPSBoy/CE, started out with the same core elements as the desktop version of GPSBoy. It differs primarily in the handling of the picturebox, Comm, and Timer components. Not only do these components need to be recreated on the form when you're writing an eMbedded Visual Basic-based program, their implementation varies just enough to cause problems if you don't know what's going on. In addition, you don't have access to the Scripting.FileSystemControl COM object. Several constants you've learned to love aren't accessible (pi is still missing). Even some calls work differently than you would expect.
Consider the picturebox control. In the desktop version of Visual Basic, you draw lines, circles, and points like this:
The Windows CE version of the picturebox control, however, uses a more standard syntax for these operations:
pic.Line (x1,y1)-(x2, y2), RGB(rr, gg, bb)
pic.Circle (x1,y1), 45, RGB(rr, gg, bb)
pic.PSet (x, y), RGB(rr, gg, bb)
This isn't a big deal when you're writing a program from scratch. When you're porting code from Visual Basic 6.0, however, it's a minor annoyance. Since previous versions of the Windows CE Toolkit for Visual Basic used these same controls, porting code from that product doesn't present the same problem.
pic.DrawLine x1, y1, x2, y2, RGB(rr, gg, bb)
pic.DrawCircle x1, y1, 45, RGB(rr, gg, bb)
pic.DrawPoint x, y, RGB(rr, gg, bb)
The eMbedded Visual Basic version of the Comm control, which is really the engine of this program, is implemented differently from its desktop counterpart. The embedded version of the Comm control supports an event called OnComm. When the value of the control's CommEvent property changes, indicating that something has been received, sent, or an error has occurred, the OnComm event is called. Instead of polling the port, you can wait for the data to come to you.
Over the years, some common constants have evolved into a natural part of the Visual Basic language. One that comes to mind is vbCrLf—the standard ASCII end-of-line marker. If you want to use this in eMbedded Visual Basic, you have to Dim it at the top of your module, then assign a value to it as soon as possible.
When you're testing a program on your device, if you make a mistake like forgetting to define vbCrLf, you'll soon find that while tracing through your program during debugging, the traces keep disappearing down a wormhole and reappearing at the line of code that called the routine you were in. This is confusing the first few dozen times it happens, but you can also bet that something's wrong with the last executed line of code in the bouncy routine.
When I was testing my code, I discovered that the Left and Right string operators, something that any programmer using Visual Basic relies upon, didn't work properly in my release of eMbedded Visual Basic! I've been using Visual Basic for a decade now, so it was pretty clear from staring at the lines that they were written properly. They just didn't work. This only came to light after I fell through numerous trace wormholes. Frustrated with a line that just didn't work, I converted the Left command to a Mid command and it began to work properly. I later found that this was a documented problem. The Left command doesn't work in a form's code because eMbedded Visual Basic thinks you're using the form's Left property. You can either use the Mid command or move the code containing the Left command to a module. More information is available in Knowledge Base article Q180455.
Debugging Tips To debug an application built for Windows CE you can connect your machine to the device directly (through either USB, serial, or infrared) and use the ActiveSync transport, or you can put your program through its paces in a desktop emulator. Each approach has advantages, and each is appropriate at a different stage of the development process.
When you work with the Windows CE emulator, you have the advantage of not having to hook up your Pocket PC while you're working. You get a virtual device directory in a section of the desktop machine's file system so you can easily move files back and forth. It's also faster to work on GUI layouts when you're not juggling two machines. The form layout tool in eMbedded Visual Basic is good, but it doesn't take into account the space taken up by the menu bars. You might have to do several iterations of a design before you get the controls placed just right.
However, when you use the emulator, you also lose some debugging power. The remote tools provided by eMbedded Visual Basic—the heap walker, process viewer, registry editor, and so on—don't work under emulation. When you want to fine-tune your program, or watch how it eats up memory in the real world, you need to use the real device. (Emulation doesn't reflect the dynamic nature of rapidly dwindling memory situations, and you always seem to have 16MB left.)
I encountered a strange problem when I was debugging a program and I aborted it abnormally. When I tried to restart it from the development environment, I received the error shown in Figure 8. I still don't know what 0xLX means, but it's sure not hex!
Figure 8 0xLX Error
It's not all bad news. Most of what you might want to use in eMbedded Visual Basic is right in there. For instance, the Common Dialog control is still available, so I could use it in GPSBoy/CE the same way as I did in GPSBoy.
Packaging for Distribution After you've written your application for the Pocket PC, the next step is to actually package it in some sort of friendly distribution unit. When you write a desktop app, this process is well-defined. Bundle up all the components, use an advanced program like InstallShield to create an installation program, and ship it. However, when targeting the Pocket PC there are a number of issues to keep in mind.
For instance, not all Pocket PCs are the same. Although they're based on the same operating system, you need to create an installation package unique to the processor you're targeting. For instance, my HP Jornada has a processor known as an SH-3. The Casio uses a MIPS VR41xx processor and the Compaq uses an ARM SA-1110, but you should check the online documentation for each product you're targeting.
Since the eMbedded Tools are, in theory, embedded, I like to give users the option of downloading just my program's runtime or the runtime plus the associated files. The runtime alone is easy—just compile into a .vb file and put it up on your Web site somewhere. To distribute an entire application, you need to package it and its redistributable components into a CAB file.
Fortunately, eMbedded Visual Basic provides a wizard that does this for you. The Application Install Wizard (found in the Tools | Remote Tools menu) creates a CAB file which contains a setup program, an INF file (to direct the setup), any OCX files needed for the installation, and any other dependent files.
The good thing is that you don't have to recompile your eMbedded Visual Basic project for each processor type. The .vb output itself is p-code, so you just need to get the proper version of the runtime files (vb.exe, vbscript.dll, vbsen.dll, pvbrt.dll, and regsvr.exe) onto the system. You can't assume that all this stuff will be on the target—it's a lot like distributing Visual Basic and OLE-based apps in 1994, where you'd have to include a megabyte of OLE system files in your installation disks because not everyone had them on their machines yet.
Why Do You Want To Do This? It seems like it could be a schlep to port your application to a handheld device like the Pocket PC. However, I'm firmly convinced that these things are taking off. I received more mail from people who were interested in programming for the devices within the first month of the release of Windows CE 3.0 than I saw for all the versions combined over the previous four years. The Pocket PC design is extensible, it lets you do some really interesting stuff and, best of all, the learning curve is relatively flat because you can use tools with which you're already familiar.