Sunday, September 9, 2012

Working with Bluetooth and GPS: Part 1 - Reading Wireless Serial Port Data

For some developers, working with wireless technologies can be daunting -- and sometimes downright intimidating. All communication is wireless, so you can't just "look up" and see, for instance, 1 MB of data going by. In addition, it is really difficult to debug wireless applications once they are deployed to a mobile device, since you don't have access to system traces or log files to pinpoint the errors while the application is running.
This technical article addresses the following tasks:
  • Helps demystify some wireless concepts using Bluetooth and the JSR-82 API
  • Shows how to run and debug Java ME Bluetooth applications on your desktop computer
  • Explains how to read data from a Bluetooth-enabled GPS device
Contents
 
The Big Problem: Where Emulators Fail
Setting Up the Environment
Unraveling Some Mysteries of Bluetooth and JSR-82
Summary
 
 
I love using the Sun Wireless Toolkit for CLDC. It is a great tool, and it's very handy when I need to create Java ME applications that adhere to the latest and greatest Java ME JSR specifications. However, the Java ME emulator in the Sun Wireless Toolkit has no means to access or communicate with actual Bluetooth hardware, which means that I don't have the ability to fully test my application with the Sun Wireless Toolkit after I start to make API calls that rely on functioning Bluetooth hardware. Because of this problem, developers are left in a difficult situation when they need to develop, test, and debug JSR-82 applications.
Additionally, testing your Java ME Bluetooth applications directly on your JSR-82 device is impractical since you don't have access to the System.out for simple debugging of your application. Additionally, the iteration cycles for developing, compiling, provisioning, and installing the application on a mobile phone is very time consuming.
The good news is that you're going to learn how to construct a low-cost solution that allows you to install, debug, and test your JSR-82 applications on your computer. I'm going to introduce to you the Mpowerplayer, a CLDC emulator for the computer that can be configured to behave like a JSR-82 Bluetooth-enabled phone. With this configuration, the Mpowerplayer will behave just like a JSR-82 Bluetooth-enabled mobile phone, but you'll have access to the System.out and have the ability to view stacktraces, both of which are essential in debugging your wireless application.

 
The following list shows the materials that you need to run the example code provided later in this article:
  • Required

    • The Sun Wireless Toolkit for CLDC. This tool is the defacto standard for developing Java ME applications.
    • A Bluetooth-enabled computer. The Bluetooth module for your PC could either be built-in to the computer or can be attached externally via the USB port.
    • The Mpowerplayer. This free desktop application is a very good mobile emulator. It is able to access your Bluetooth hardware on your desktop PC, to execute the JSR-82 method calls that require access to actual Bluetooth hardware.
    • REQUIRED - A JSR-82 library that supports the Mpowerplayer. I use the Avetana SDK, which works beautifully with the Mpowerplayer.
    • A Bluetooth-enabled GPS device. I use the DeLorme Earthmate BT-20.
  • Optional

    • A Java ME phone that supports the JSR-82 API. If you don't know whether your phone supports the JSR-82 API, be sure take a look at this list. It is the best source of information to determine what Java ME APIs the major phone manufacturers support. However, I did notice that the list didn't include any RIM Blackberry devices, which also support the JSR-82 specification.
The developer version of the Mpowerplayer is available as a zip file. All you need to do is unzip the file in the location that you desire to install it. Now, for the Mpowerplayer to get access to the Bluetooth device on your PC, copy the avetanaBluetoth.jar file to the "mpp-sdk\bluetooth" folder - it's just that simple! Later on, you're going to see screenshots of Mpowerplayer in action.

 

Did you know that once you discover the connection URL for your desired Bluetooth service, then you no longer need to employ the device- and service-discovery processes? If you're unfamiliar with what a Bluetooth connection URL looks like, I have provided an example below:
btspp://001AA3000C19:1;authenticate=false;encrypt=false;master=false
Let us briefly revisit the purposes of the device-discovery and service-discovery processes that apply to all Bluetooth-enabled systems, whether or not if you use the JSR-82 API. The device-discovery process is used to determine what Bluetooth devices are in the vicinity. In the connection URL listed previously, the device represented has the Bluetooth address of 001AA3000C19. In the example code that will be presented later in this article, I used an inner class named BTUtility that implements all the necessary JSR-82 Bluetooth API code for device and service discovery. The code snippet below shows the necessary steps for device discovery:
        public BTUtility() {
            try {
                LocalDevice localDevice = LocalDevice.getLocalDevice();
                discoveryAgent = localDevice.getDiscoveryAgent();
                discoveryForm.append(" Searching for Bluetooth devices in the vicinity...\n");
                discoveryAgent.startInquiry(DiscoveryAgent.GIAC, this);

            } catch(Exception e) {
                e.printStackTrace();
            }
        }

        public void deviceDiscovered(RemoteDevice remoteDevice, DeviceClass cod) {
            try{
 		   discoveryForm.append("found: " + remoteDevice.getFriendlyName(true));
            } catch(Exception e){
               discoveryForm.append("found: " + remoteDevice.getBluetoothAddress());
            } finally{
		   remoteDevices.addElement(remoteDevice);
		}
        }

        public void inquiryCompleted(int discType) {

            if (remoteDevices.size() > 0) {

                // the discovery process was a success
                // so let's out them in a List and display it to the user
                for (int i=0; i<remoteDevices.size(); i++){
                    try{
                       devicesList.append(((RemoteDevice)remoteDevices.elementAt(i)).getFriendlyName(true), bt_logo);
                    } catch (Exception e){
                       devicesList.append(((RemoteDevice)remoteDevices.elementAt(i)).getBluetoothAddress(), bt_logo);
                    }
                }
                display.setCurrent(devicesList);
            } else {
			// handle this
		}
 
The inner class itself implements the DiscoveryListener interface, so its deviceDiscovered() method will be called every time a remote Bluetooth device has been found. When the device-discovery process has finally ended, the JVM will call the inquiryCompleted() method. Fortunately, I don't have to deploy this application to my JSR-82 enabled phone to properly test it. I can test the entire application on my desktop computer using the Mpowerplayer, as described earlier.

Figure 1 shows the Mpowerplayer running my application during the device-discovery process, and Figure 2 shows the state of mobile application after the device-discovery process is finished.

Now that we've taken care of the device-discovery process, and we see that the device that we want to connect to is in the list of available devices, let's take another look at the fully qualified connection URL.
btspp://001AA3000C19:1;authenticate=false;encrypt=false;master=false
As you can see, the device-discovery process lets us know the Bluetooth address (in this case, 001AA3000C19) and the friendly name of the remote Bluetooth device (in this case, Earthmate BT-20 GPS). But we still don't know what the other parameters are that comprise the connection URL. After all, a single Bluetooth device can offer multiple services -- for instance, a Bluetooth Access point can offer both Dialup Networking and Personal Area Networking services. Therefore, we need to search for the appropriate service that we want on the selected Bluetooth device.

The service-search process is dependent on knowing the type of service that you want. I want to consume serial data from a Bluetooth-enabled GPS device, and the unique identifier for wireless serial services is 0x1101. The previously mentioned inner class, BTUtility, has also implemented all the code for service searching. The following snippet shows what is involved.
        public void run(){

            try {
                RemoteDevice remoteDevice = (RemoteDevice)remoteDevices.elementAt(devicesList.getSelectedIndex());
                discoveryAgent.searchServices(attrSet, uuidSet, remoteDevice , this);

            } catch(Exception e) {
                e.printStackTrace();
            }
        }

        public void servicesDiscovered(int transID, ServiceRecord[] servRecord){

            for(int i = 0; i < servRecord.length; i++) {

                DataElement serviceNameElement = servRecord[i].getAttributeValue(0x0100);
                String _serviceName = (String)serviceNameElement.getValue();
                String serviceName = _serviceName.trim();
                btConnectionURL = servRecord[i].getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);

            }
            display.setCurrent(readyToConnectForm);
		readyToConnectForm.append("\n\nNote: the connection URL is: " + btConnectionURL);
		System.out.println("Note: the connection URL is: " + btConnectionURL);

        }

        public void serviceSearchCompleted(int transID, int respCode) {

            if (respCode == DiscoveryListener.SERVICE_SEARCH_COMPLETED) {
                // the service search process was successful

            } else {
                // the service search process has failed
            }

        }
 
Now, service searching is a blocking I/O operation, so I put the intensive work in the run() method of a thread to allow the application to behave nicely. Whenever a matching service has been found on the remote device, the JVM will call my servicesDiscovered() method to let me know so that I can do something about it. As shown in the following figure, I've found the service that I want, and I have everything that I need to get the connection URL.
 
To reiterate a previous point: now that you have determined the connection URL for your desired device, you no longer need to go through the device- and service-discovery processes for subsequent usage of the remote Bluetooth device. All you need to do is open a connection on the URL. After that, you have everything that you need to communicate with the remote Bluetooth device.
Figure 4 shows the operation of another thread-enabled inner class that opens the connection on the URL and then reads the data from the wireless serial port.
 
Wait a minute. If we're reading serial data from a GPS device, then where are the latitudes, longitudes, and other global-positioning stuff? Is the data corrupted?
Actually, the serial data that you see in Figure 4 is actually encoded in NMEA (National Marine Electronics Association) format, which is the common format for all GPS devices. Part 2 of this technical article shows you how to decode the NMEA data and plot your location on your phone.

Link: http://dsc.sun.com/mobility/apis/articles/bluetooth_gps/part1/

No comments:

Post a Comment