(edit sidebar)
Intro to Physical Computing Syllabus

Research & Learning

Other Class pages

Shop Admin

ITP Help Pages
Tom's pcomp site
DanO's pcomp site


Reading A Serial Protocol

Many applications are best realized NOT by making your own devices, but by using existing hardware. For example, you could build your own GPS receiver for a couple hundred dollars and a few dozen hours of sweating over a circuit board, or you could buy a cheap one off the shelf. GPS receivers are all supposed to be able to communicate using one of several standard protocols. To use an off-the-shelf unit in your application, you'd need to program your computer or microcontroller to read whichever protocol your receiver speaks.

There are a few parameters to consider when learning to use an existing serial protocol:

  • what are the physical connections, and can you get or make the appropriate connector?
  • What are the electrical levels used? TTL? RS-232? RS-485? Something else? What do you need to match them to your controller?
  • Is the protocol ASCII-based or binary?
  • Is it a handshaking (call-and-response) protocol, or does the device just send data continually?
  • Is the protocol specification available publicly? Has someone else already written an example program for it, to save you time?

This example shows how use Processing to read a GPS receiver that's using the NMEA 0183 protocol. the principles shown here can be applied generically to many serial protocols.

Get the physical and electrical interface right

The GPS receiver used here is a Garmin GPS72, which has an RS-232 serial port. It also has a simulation mode, in which it sends out "fake" GPS readings that conform to the protocol. This is useful, since GPs doesn't work indoors, so testing with a receiver that doesn't have a simulation mode would be difficult. You'd have to program outdoors. Furthermore, it shows the readings onscreen, so you can check your serial data against what's onscreen.

Since many laptops no longer have RS-232 serial ports, so you'd need an RS-232 to USB converter to make this work. Briefly, the system here is:


Understand the protocol's structure, and look at it

The NMEA FAQ explains the NMEA protocol well, including this summary:

Under the NMEA-0183 standard, all characters used are printable ASCII text (plus carriage return and line feed). NMEA-0183 data is sent at 4800 baud.
The data is transmitted in the form of "sentences". Each sentence starts with a "$", a two letter "talker ID", a three letter "sentence ID", followed by a number of data fields separated by commas, and terminated by an optional checksum, and a carriage return/line feed. A sentence may contain up to 82 characters including the "$" and CR/LF.

In other words, it operates at 4800 bits per second, it's all in ASCII, and each sentence ends with a carriage return and a linefeed. The simplest way to see if you can read it is to open a serial terminal program. Open up Hyperterminal, zTerm, or screen and see what comes in. You'll get something like this:

$GPRMC,175822,V,4043.8533,N,07359.7486,W,0.0,0.0,010606,13.4,W,S*29
$GPRMB,V,9.99,R,,GARMIN,3851.3330,N,09447.9410,W,966.357,270.1,,V,S*69
$GPGGA,175822,4043.8533,N,07359.7486,W,8,11,2.0,20.6,M,-34.0,M,,*45
$GPGLL,4043.8533,N,07359.7486,W,175822,V,S*50
$GPBOD,257.4,T,270.8,M,GARMIN,*50
$GPBWC,175822,3851.3330,N,09447.9410,W,270.1,T,283.5,M,966.357,N,GARMIN,S*75
$GPVTG,0.0,T,13.4,M,0.0,N,0.0,K*78
$GPXTE,V,V,9.99,R,N,S*06
$PGRME,15.0,M,22.5,M,27.0,M*1A
$PGRMZ,68,f,3*25
$PGRMM,WGS 84*06

You know from the description above that each line is a sentence. Reading further in the NMEA FAQ, you learn that there are several formats of the sentence, and that the first element ($GPRMC, $GPGLL, etc) describes the sentence. You'd need to read the descriptions o the sentences to see which one contains the data you need. If all you need is latitude and longitude, the $GPGLL sentence looks easy:

  GLL - Geographic position, Latitude and Longitude
        GLL,4916.45,N,12311.12,W,225444,A
           4916.46,N    Latitude 49 deg. 16.45 min. North
           12311.12,W   Longitude 123 deg. 11.12 min. West
           225444       Fix taken at 22:54:44 UTC
           A            Data valid

In other words, the first item in the sentence names the sentence, the second is latitude, the third identifies north/south, the fourth is longitude, the fifth identifies east/west, and the sixth is the time of the reading. Each item is separated by a comma. So you can already write an outline for a program to read it, like so:

Read data into a string until you get a carriage return
Separate the string into individual elements at the commas
Put the second element into a variable called @@latitude@@
Use the third element to determine north/south
Put the fourth element into a variable called @@longitude@@
Use the fifth element to determine east/west

Write a program that just reads the protocol

To convert this into code, start by just reading the serial data in until you get a carriage return. Following is the Processing code:


// import the serial library:
import processing.serial.*;

int linefeed = 10;       // linefeed in ASCII
int carriageReturn = 13; // carriage return in ASCII
Serial myPort;           // The serial port

void setup() {
  // List all the available serial ports
  println(Serial.list());

  // I know that the first port in the serial list on my mac
  // is always my  Keyspan adaptor, so I open Serial.list()[0].
  // Open whatever port is the one you're using.
  myPort = new Serial(this, Serial.list()[0], 4800);

  // read bytes into a buffer until you get a carriage return (ASCII 13):
  myPort.bufferUntil(carriageReturn);
}

void draw() {
  // not doing anything here
}

// serialEvent  method is run automatically by the Processing applet
// whenever the buffer reaches the  byte value set in the bufferUntil() 
// method in the setup():

void serialEvent(Serial myPort) { 
  // read the serial buffer:
  String myString = myPort.readStringUntil(linefeed);
  // if you got any bytes other than the linefeed:
  if (myString != null) {
    // print it:
    println(myString);
  }
} 

Add a method to parse the data once you know you have it

Once you've got the string, you can pass it to a method to parse the string. First, add a few global variables at the beginning of the program:

float latitude = 0.0;    // the latitude reading in degrees
String northSouth;       // north or south?
float longitude = 0.0;   // the longitude reading in degrees
String eastWest;         // east or west?

Then replace println(myString); above with parseString(myString);

Then add this method at the end of the program:

void parseString(String serialString) {
  // split the string at the commas
  //and convert the sections into integers:
  String items[] = (split(serialString, ','));
  // if the first item in the sentence is our identifier, parse the rest:
  if (items[0].equals("$GPGLL") == true) {
    // move the items from the string into the variables:
    latitude = float(items[1]);
    northSouth = items[2];
    longitude = float(items[3]);
    eastWest = items[4];
    println(latitude + northSouth + "," +longitude + eastWest);
  }
}

That's it!

  Edit | View | History | Print | Recent Changes | Search Page last modified on June 01, 2006, at 03:33 PM