|
Intro to Physical Computing Syllabus Research & Learning Other Class pages
ITP Help Pages |
Reading A Serial ProtocolMany 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:
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%lfloat margin=10 alt='Garmin GPS72 reader screen'% 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 itThe 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 protocolTo 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 itOnce 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 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! |