Pages: [1] 2   Go Down
Author Topic: GPS EM-406 example  (Read 12257 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Full Member
***
Karma: 0
Posts: 183
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
//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
  
}
Logged

Florida, USA
Offline Offline
Full Member
***
Karma: 0
Posts: 146
meow!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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++)
Logged

Daniel
Guest
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
 

« Last Edit: December 13, 2006, 07:10:43 pm by Daniel » Logged

0
Offline Offline
Full Member
***
Karma: 0
Posts: 183
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Underhill Center, Vermont, USA
Offline Offline
Jr. Member
**
Karma: 0
Posts: 71
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

0
Offline Offline
Full Member
***
Karma: 0
Posts: 183
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

0
Offline Offline
Newbie
*
Karma: 0
Posts: 4
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Brisbane, Australia
Offline Offline
God Member
*****
Karma: 1
Posts: 593
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Daniel
Guest
 Bigger Bigger  Smaller Smaller  Reset Reset

@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
« Last Edit: October 16, 2007, 02:20:55 am by Daniel » Logged

0
Offline Offline
Full Member
***
Karma: 0
Posts: 183
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 4
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Sure the following is the code used:

Code:
//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.

Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 4
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
#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

Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 17
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Australia
Offline Offline
Jr. Member
**
Karma: 0
Posts: 99
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

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

Code:
return val & 0x7f;
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 2
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Logged

Pages: [1] 2   Go Up
Jump to: