Thursday, January 6, 2011

GpsSPOT

Today I hooked up my GPS module (EM-406A) to the SunsPOT. I already did this a while ago, but I wanted to simplify my old circuit and share the procedure of how to do it.

There are already some good blog entries and resources on how to connect a GPS module to the SPOT. There is for example the blog of David G. Simmons, who is one of the team members of the Oracle Labs team which develops the devices. I used some of his code snippets to read the GPS message via UART. The product guide from sparkfun described the pin layout of the module which was necessary to connect the module correctly.

You also might need some minor adjustments for the determined coordinates. My module was off a little bit so I adjusted the readings by checking against google maps.

This is a simple schematic on how to connect everything on a protoboard. I used some resistors as a safety measure. You might not need them, but I got cautious as I destroyed components in the past.


Here is the source code. It is mostly taken from David's blog post but I modified it a bit to match my needs. For processing all GPS formats look into his blog for an example.

GpsSPOT class:
import com.sun.spot.peripheral.Spot;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.util.Utils;

public class GpsSPOT extends MIDlet {

    private EDemoBoard board;

    protected void startApp() throws MIDletStateChangeException {
            new com.sun.spot.service.BootloaderListenerService().getInstance().start();
            board = EDemoBoard.getInstance();
            board.initUART(4800,false);
            LEDIndicator.showSPOTStarted();
            while(true) {
                String rawMessage = getGpsMessage();
                System.out.println("Raw message: " + rawMessage);
                System.out.println("Parsed message: \n" + new GPGGAMessage(rawMessage).toString());
                Utils.sleep(2000);
            }
    }

    protected void pauseApp() {
    }

    protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
        Spot.getInstance().getSleepManager().enableDeepSleep();
    }

    private String getGpsMessage() {
        char val;
        String message = "";
        byte thresholdCounter = 0;
        while (thresholdCounter < 10) {
            try {
                val = (char) board.readUART(100);
                if (val == '\n') {
                    if (message.charAt(0) == '$') {
                        if (message.startsWith("$GPGGA")) {
                                                    LEDIndicator.showSuccessful();
                                                    return message;
                        }
                        message = "";
                    } else {
                        message = "";
                    }
                } else {
                    // recover from bad read .. reset
                    if (val == '$') {
                        message = "";
                    }
                    message += (char) val;
                }
            } catch (Exception e) {
                thresholdCounter++;
                LEDIndicator.showException();
                Utils.sleep(50);
            }
        }
        return "";
    }
}
GpsParser class:
import java.util.Hashtable;

/**
 * Parses the Gps Messages. Right now only in GPGGA format.
 */
public class GpsParser {

    public static Hashtable parseMessage(String m, String[] fields){
        Hashtable thisHash = new Hashtable();
        String msg = m;
        int nToke =0;
        int del = m.indexOf(',');
        msg = msg.substring(del+1); // scrub the header
        del = msg.indexOf(',');
       for(int x = 0; x< fields.length;x++){
             String val = msg.substring(0,del);
             thisHash.put(fields[x], val);
            if(del+1 <= msg.length())
                 msg = msg.substring(del+1);
             del = msg.indexOf(',');
            if(del < 0){
                if(msg.indexOf('*')>0){
                     del = msg.indexOf('*');
                }else {
                     del = msg.length();
                }
            }
         }
         return thisHash;
     }
}
GPGGAMessage class:
import java.util.Enumeration;
import java.util.Hashtable;


public class GPGGAMessage {

    static final String[] GPGGAfields = {"UTCTime", "Latitude", "North-South", "Longitude", "East-West", "Position-Fix", "Satellites-Used", "HDOP", "Altitude", "Alt-Units", "GEOID", "GEOID-Units",
        "Age-of-Diff", "Diff-Station-ID", "Checksum"};
    private Hashtable parsedMessage;

    public GPGGAMessage(String message) {
        parsedMessage = GpsParser.parseMessage(message, GPGGAfields);
    }

    public String getUTCTime() {
        return getValue("UTCTime");
    }

    public String getLatitude() {
        return getValue("Latitude");
    }

    public String getLongitude() {
        return getValue("Longitude");
    }

    public String getSatellitesUsed() {
        return getValue("Satellites-Used");
    }

    public String getHDOP() {
        return getValue("HDOP");
    }

    public String getAltitude() {
        return getValue("Altitude");
    }

    public String getGeoid() {
        return getValue("GEOID");
    }

    public String getChecksum() {
        return getValue("Checksum");
    }

    public String getValue(String key) {
        return (String) parsedMessage.get(key);
    }

    public String toString() {
        StringBuffer buffer = new StringBuffer();
        Enumeration enumeration = parsedMessage.keys();
        while(enumeration.hasMoreElements()) {
            String key = (String)enumeration.nextElement();
            String value = (String)parsedMessage.get(key);
            buffer.append(key).append(": ").append(value).append("\n");
        }
        return buffer.toString();
    }
}
 LEDIndicator class:
import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.sensorboard.peripheral.ITriColorLED;
import com.sun.spot.sensorboard.peripheral.LEDColor;
import com.sun.spot.util.Utils;

public class LEDIndicator {

    public static void showSPOTStarted() {
        EDemoBoard board = EDemoBoard.getInstance();
        ITriColorLED[] leds = board.getLEDs();
        for (int x = 0; x < leds.length; x++) {
            leds[x].setColor(LEDColor.BLUE);
            leds[x].setOn();
            Utils.sleep(50);
            leds[x].setOff();
        }
    }

   

    public static void showException() {
        EDemoBoard board = EDemoBoard.getInstance();
        ITriColorLED[] leds = board.getLEDs();
        for (int x = 7; x >= 0; x--) {
            leds[x].setColor(LEDColor.RED);
            leds[x].setOn();
        }
        Utils.sleep(50);
        for (int x = 7; x >= 0; x--) {
            leds[x].setOff();
        }
    }

    public static void showSuccessful() {
        EDemoBoard board = EDemoBoard.getInstance();
        ITriColorLED[] leds = board.getLEDs();
        for (int x = 7; x >= 0; x--) {
            leds[x].setColor(LEDColor.GREEN);
            leds[x].setOn();
        }
        Utils.sleep(50);
        for (int x = 7; x >= 0; x--) {
            leds[x].setOff();
        }
    }
}
An example sensor reading would look like this:

Raw message:
$GPGGA,125608.000,5223.6384,N,01343.1545,E,1,06,2.7,37.2,M,44.5,M,,0000*6B
Parsed message:
HDOP: 2.7
Altitude: 37.2
North-South: N
Checksum: 6B
Latitude: 5223.6384
Age-of-Diff:
Satellites-Used: 06
GEOID: 44.5
East-West: E
UTCTime: 125608.000
Diff-Station-ID: 0000
GEOID-Units: M
Alt-Units: M
Position-Fix: 1
Longitude: 01343.1545
I also made a video to demonstrate the setup.

video