Go Down

Topic: GPS EM-406 example (Read 13870 times) previous topic - next topic

bigengineer

I got my GPS module working with some filtering for the right lines. I program mainly by trial and error so the code might not be elegant or by the book. But it works. I  started with the softserial example from Heather: http://www.arduino.cc/en/Tutorial/SoftwareSerial. I changed it here and there and made it to filter out a specific NMEA rule.

I was quite suprised when I discovered how easy it is to use avr-libc library.

Any comments are welcome.


Code: [Select]
//Created August 15 2006
//Heather Dewey-Hagborg
//http://www.arduino.cc
//reworked to GPS reader
//Dirk, december 2006

#include <ctype.h>
#include <string.h>

#define bit9600Delay 84  
#define halfBit9600Delay 42
#define bit4800Delay 188
#define halfBit4800Delay 94

byte rx = 6;
byte tx = 7;
byte SWval;
char dataformat[7] = "$GPRMC";
char messageline[80] = "";
int i= 0;

void setup() {
 pinMode(rx,INPUT);
 pinMode(tx,OUTPUT);
 digitalWrite(tx,HIGH);
 digitalWrite(13,HIGH); //turn on debugging LED
 SWprint('h');  //debugging hello
 SWprint('i');
 SWprint(10); //carriage return
 Serial.begin(9600);
}

void SWprint(int data)
{
 byte mask;
 //startbit
 digitalWrite(tx,LOW);
 delayMicroseconds(bit4800Delay);
 for (mask = 0x01; mask>0; mask <<= 1) {
   if (data & mask){ // choose bit
     digitalWrite(tx,HIGH); // send 1
   }
   else{
     digitalWrite(tx,LOW); // send 0
   }
   delayMicroseconds(bit4800Delay);
 }
 //stop bit
 digitalWrite(tx, HIGH);
 delayMicroseconds(bit4800Delay);
}

char SWread()
{
 byte val = 0;
 while (digitalRead(rx));
 //wait for start bit
 if (digitalRead(rx) == LOW) {
   delayMicroseconds(halfBit4800Delay);
   for (int offset = 0; offset < 8; offset++) {
     delayMicroseconds(bit4800Delay);
     val |= digitalRead(rx) << offset;
   }
   //wait for stop bit + extra
   delayMicroseconds(bit4800Delay);
   delayMicroseconds(bit4800Delay);
   return val;
 }
}
void char2string()
{
 i = 0;
 messageline[0] = SWread();
 if (messageline[0] == 36) //string starts with $
 {
   i++;
   messageline[i] = SWread();
   while(messageline[i] != 13 & i<80) //carriage return or max size
   {
     i++;
     messageline[i] = SWread();
   }
   messageline[i+1] = 0; //make end to string
 }
}
void loop()
{
 digitalWrite(13,HIGH);
//only print string with the right dataformat
 char2string();
 if (strncmp(messageline, dataformat, 6) == 0 & i>4)
 {
   for (int i=0;i<strlen(messageline);i++)
   {
     Serial.print(messageline[i], BYTE);
   }
 }
 
 //Serial.print(SWread(),BYTE); //use this to get all GPS output, comment out from char2string till here
 
}

CosineKitty

This is a good example of software serial I/O.  I did spot something potentially very inefficient:

   for (int i=0;i<strlen(messageline);i++)

this causes strlen() to be called on each iteration, counting how many characters there are in the string.  If the string is very short, it does not matter, much, but as the string gets longer and longer, the amount of CPU time consumed by the loop goes up as the square of the number of characters.  This is because there are N characters, each strlen takes order(N) time, and there are N loops, resulting in order(N*N).  Instead, I would recommend this:

   for (int i=0; messageline != '\0'; i++)

Daniel

#2
Dec 14, 2006, 12:54 am Last Edit: Dec 14, 2006, 01:10 am by Daniel Reason: 1
This is a great contribution. I have been trolling the forum lately looking for Arduino> Hardware info to use in teaching, as my students always want to hook up the oddest things...  In that light, I added your GPS code example to the Playground. Thanks.



bigengineer

Quote
for (int i=0; messageline != '\0'; i++)

I never thought about this construction, great tip, thanks!

Quote
This is a great contribution. I have been trolling the forum lately looking for Arduino> Hardware info to use in teaching, as my students always want to hook up the oddest things...  In that light, I added your GPS code example to the Playground.


This is a very simple program but it is exactly the kind of program that is useful for me to get started. So, now I finally managed to make up something myself I had to contribute.

brianbr

Quote
Quote
for (int i=0; messageline != '\0'; i++)

I never thought about this construction, great tip, thanks!

Quote
This is a great contribution. I have been trolling the forum lately looking for Arduino> Hardware info to use in teaching, as my students always want to hook up the oddest things...  In that light, I added your GPS code example to the Playground.



This is puzzling ... in char2string() you tie off messageline with a 0. So why are we adding code to do the byte-banging  of the string out tha already exists in the Serial core library????  Why not just replace this code

//----------------------------------------------------------------
 char2string();
 if (strncmp(messageline, dataformat, 6) == 0 & i>4)
 {
     for (int i=0; messageline != '\0'; i++)
   {
     Serial.print(messageline, BYTE);
   }
 }
//-------------------------------------------------------

with this code

//----------------------------------------------------------------
 char2string();
 if (strncmp(messageline, dataformat, 6) == 0 & i>4)
 {
     Serial.println(messageline);
 }
//-------------------------------------------------------

Or did I miss something?

cheers ... BBR

bigengineer

That might be easier. Sometimes you just don't see the easier solution

mopowered

First post - cool hardware BTW...

Thanks bigengineer and everyone else - I now have my EM406A talking to my Arduino Decimilla (4 wires, 5V/GND/TX/RX) using the code posted.  I even tried all three examples of how to print the resulting strings to the Debug Window.  My problem is that they all look like this:



These are printing ALL strings BTW.  I understand that the conversation from the GPS unit is 4800 baud (hence the 4800 delays) and received thru Digital pin 6 on the Arduino.  I also understand that the strings are output via the onboard debug serial lines (hardware uart that the USB FTDI chip uses - with the following in Setup(): Serial.begin(9600); ) hence them showing up at 9600 baud in the Serial Debug Monitor.  I get the same garbage if I set the output to 4800 via Serial.begin(4800); in Setup();

What am I missing here?  TIA

mopowered

Cheater

Quote
What am I missing here?  TIA

That looks very close to NEMA output so your very close.
The problem must be with the serial stuff.

Daniel

#8
Oct 16, 2007, 09:20 am Last Edit: Oct 16, 2007, 09:20 am by Daniel Reason: 1
@mopowered

I agree with Cheater: looks like you have the commas in between the data, but not the data in corect form. Check to see if your data needs to go through an inverter like the 74LS04 (sometimes you need to invert RS232 TTL-level data in order to read it), and also  check the serial port speed in pull-down menu in the serial monitor window.

D

bigengineer

Strange, the comma's and $'s are ok but the characters are not. Can you post your code? Or did you just use the code without any modifications?

One of these days I should make an example program with the software serial library.

mopowered

Sure the following is the code used:

Code: [Select]
//Created August 15 2006
//Heather Dewey-Hagborg
//http://www.arduino.cc
//reworked to GPS reader
//Dirk, december 2006

#include <ctype.h>
#include <string.h>

#define bit9600Delay 84  
#define halfBit9600Delay 42
#define bit4800Delay 188
#define halfBit4800Delay 94

byte rx = 6;
byte tx = 7;
byte SWval;
char dataformat[7] = "$GPRMC";
char messageline[80] = "";
int i= 0;

void setup() {
 pinMode(rx,INPUT);
 pinMode(tx,OUTPUT);
 digitalWrite(tx,HIGH);
 digitalWrite(13,HIGH); //turn on debugging LED
 SWprint('h');  //debugging hello
 SWprint('i');
 SWprint(10); //carriage return
 Serial.begin(9600);  // this is used to echo what is read from the GPS out the USB->SerialDebugMonitor
}

void SWprint(int data)
{
 byte mask;
 //startbit
 digitalWrite(tx,LOW);
 delayMicroseconds(bit4800Delay);
 for (mask = 0x01; mask>0; mask <<= 1) {
   if (data & mask){ // choose bit
     digitalWrite(tx,HIGH); // send 1
   }
   else{
     digitalWrite(tx,LOW); // send 0
   }
   delayMicroseconds(bit4800Delay);
 }
 //stop bit
 digitalWrite(tx, HIGH);
 delayMicroseconds(bit4800Delay);
}

char SWread()
{
 byte val = 0;
 while (digitalRead(rx));
 //wait for start bit
 if (digitalRead(rx) == LOW) {
   delayMicroseconds(halfBit4800Delay);
   for (int offset = 0; offset < 8; offset++) {
     delayMicroseconds(bit4800Delay);
     val |= digitalRead(rx) << offset;
   }
   //wait for stop bit + extra
   delayMicroseconds(bit4800Delay);
   delayMicroseconds(bit4800Delay);
   return val;
 }
}
void char2string()
{
 i = 0;
 messageline[0] = SWread();
 if (messageline[0] == 36) //string starts with $
 {
   i++;
   messageline[i] = SWread();
   while(messageline[i] != 13 & i<80) //carriage return or max size
   {
     i++;
     messageline[i] = SWread();
   }
   messageline[i+1] = 0; //make end to string
 }
}

void loop()
{
 digitalWrite(13,HIGH);
 
 //
 // 1st way to print only the $GPRMC message
 //
 
 //only print string with the right dataformat
 // char2string();
 // if (strncmp(messageline, dataformat, 6) == 0 & i>4)
 // {
 //   for (int i=0; messageline[i] != '\0'; i++)
 //   {
 //      Serial.print(messageline[i], BYTE);
 //   }
 // }
 
 //
 // 2nd way to print only the $GPRMC message
 //
 
 //   char2string();
 //   if (strncmp(messageline, dataformat, 6) == 0 & i>4)
 //   {
 //     Serial.println(messageline);
 //   }
 
 //
 // way to print ALL RECEIVED MESSAGES!
 //
 
  Serial.print(SWread(),BYTE); //use this to get all GPS output, comment out from char2string till here
 
}



The last bytewise print of the raw stream gave the best results of the 3 different ways to print output strings.


mopowered

Must be a problem in the SoftwareSerial read routine because I got the exact same results (unreadable nmea-like strings) with the following code - here I'm only using the software serial for the read part and the hardware uart for the write results to debug console.  You'll notice in the main loop() the same results come from either the readbyte/printbyte method or readline/printline method (commented out).  I also changed the name of the char2string function to getLine:

Code: [Select]

#include <ctype.h>

#define bit9600Delay 84  
#define halfBit9600Delay 42
#define bit4800Delay 188
#define halfBit4800Delay 94

byte rx = 6;   // EM406A GPS tx line connected to pin 6 on Arduino (4800baud)
byte SWval;
char line[80]="";

void setup() {
 pinMode(rx,INPUT);
 digitalWrite(13,HIGH);     // turn on debugging LED
 Serial.begin(4800);        // Setup USB serial port monitor to 4800baud
}

int SWread()
{
 byte val = 0;
 while (digitalRead(rx));
 //wait for start bit
 if (digitalRead(rx) == LOW) {
   delayMicroseconds(halfBit4800Delay);
   for (int offset = 0; offset < 8; offset++) {
    delayMicroseconds(bit4800Delay);
    val |= digitalRead(rx) << offset;
   }
   //wait for stop bit + extra
   delayMicroseconds(bit4800Delay);
   delayMicroseconds(bit4800Delay);
   return val;
 }
}

void getLine()
{
 int i = 0;
 line[0] = SWread();
 if (line[0] == 36) //string starts with $
 {
   i++;
   line[i] = SWread();
   while(line[i] != 13 & i<80) //carriage return or max size
   {
     i++;
     line[i] = SWread();
   }
   line[i+1] = 0; //make end to string
 }
} // end getLine()


void loop()
{
   SWval = SWread();
   Serial.print(SWval);
   
   // getLine();
   // Serial.println(line);
   
}


Am currently using the code at the following URL with perfect results using the EM-406 instead of the Parallax module (also connected via 3 wires pwr/gnd/tx):

http://www.arduino.cc/playground/Tutorials/GPS


jnguyen327

In regards to mopowered's problem, I checked the decimal value of those characters you were getting and it looks like they're all off by 128. Try something like a

if( val > 128 )
 return val-128;
else
 return val;

It's hacky but it's a quick check to see if you can get good characters from it. I remember running into a problem with chars and bytes before in regards to this 128 offset.

crimony

Quote
if( val > 128 )
 return val-128;
else
 return val;


which hopefully your compiler will be clever enough to convert to:

Code: [Select]
return val & 0x7f;

toggie

hej, does i understand this right? it reads the nmea and then seriel print it?


Go Up