1

I am trying to get GPS data from my GY-NEO6MV2, but the results are not readable with SoftwareSerial or other libraries. I have used the following code that has worked for others on the net, but I don't get any results from it. The GPS data appears perfectly in the serial monitor, but the interfaces such as SoftwareSerial fail to receive it. Instead, I receive a bunch of -1s. Here is the code:

#include "SoftwareSerial.h"

// Constants
#define txPin 1      //tx pin in GPS connection
#define rxPin 2      //rx pin in GPS connection

// Set up the GPS serial port
SoftwareSerial gps = SoftwareSerial(rxPin, txPin);
// Variables
byte byteGPS = 0;
int i = 0;
int state = 0;
char dataGPG[100] = "";
char *pch;
char *GGA[15];
int sat = 0;
void setup() {
  //setup for Serial Port
  Serial.begin(9600);
  Serial.flush();
  //setup for GPS Serial Port
  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);
  gps.begin(4800);
  gps.flush();
  //setup satellites signal
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);
  // Turn off the led until a satellite signal
}

void loop() {
  gps.flush();
  Serial.flush();
  Serial.println("Waiting GPS data...");
  // Prepare all for reading GPS Serial Port
  memset(dataGPG, 0, sizeof(dataGPG)); // Remove previous readings
  byteGPS = 0; // Remove data
  byteGPS = gps.read(); // Read the byte that is in the GPS serial port
  delay(1000);
  // Find the desired string
  while (byteGPS != '$') {
    byteGPS = gps.read();
   /******* Here I just get -1 *************/
  }
  // Save the string in an array
  i = 1;
  dataGPG[0] = '$';
  while (byteGPS != '*' ) {
    byteGPS = gps.read();
    dataGPG[i] = byteGPS;
    i++;
  }
  dataGPG[i] = '\0';
  string(); // Call to the function that manipulates our string
}

/*
  This function will allow us to identify the data we need to get the longitude, latitude ...
*/

void string() {
  i = 0;
  memset(GGA, 0, sizeof(GGA)); // Remove previous readings
  pch = strtok (dataGPG, ",");
  // Analyze the saved interval in pch to see if it the needed string
  if (strcmp(pch, "$GPGGA") == 0) {
    while (pch != NULL) {
      pch = strtok (NULL, ",");
      GGA[i] = pch;
      i++;
    }
    plot(GGA, "$GPGGA");
    // Call to the function that is going to display the data
  }
}

/*
  This function organize the gps data received for printing in the serial monitor.
*/

void plot(char **GGAPrint, char *trama) {
  state = atoi(GGAPrint[5]);
  sat = atoi(GGAPrint[6]);
  if (trama == "$GPGGA" && state == 1) {
    digitalWrite(13, HIGH);
    // Then there are satellites, the LED switch ON
    Serial.println("");
    Serial.println("----------------------------------------------");
    Serial.print("UTC Hour -> ");
    Serial.println(GGAPrint[0]);
    Serial.print("Latitude -> ");
    Serial.print(GGAPrint[1]);
    Serial.println(GGAPrint[2]);
    Serial.print("Longitude -> ");
    Serial.print(GGAPrint[3]);
    Serial.println(GGAPrint[4]);
    Serial.print("GPS quality: 0=null; 1=GPS fixed -> ");
    Serial.println(GGAPrint[5]);
    Serial.print("Number of satellites -> ");
    Serial.println(sat);
    Serial.print("Horizontal Dilution of Precision -> ");
    Serial.println(GGAPrint[7]);
    Serial.print("Antenna altitude -> ");
    Serial.print(GGAPrint[8]);
    Serial.println(GGAPrint[9]);
    Serial.print("Geoid Separation -> ");
    Serial.print(GGAPrint[10]);
    Serial.println(GGAPrint[11]);
    Serial.println("----------------------------------------------");
    Serial.println("");
  } else {
    // If no satellite connection
    digitalWrite(13, LOW); // Turn off the LED
    Serial.println("");
    Serial.println("-----------------------------");
    Serial.print("|--- Satellites Used -->");
    Serial.print(sat);
    Serial.println(" |");
    Serial.println("|----Waiting location----|");
    Serial.println("-----------------------------");
    Serial.println("");
  }
}

The result wasn't correct and I only saw this sentence:

Waiting GPS data...


I updated the code by adding gps.available() and delay(2) before every gps.read() and it worked. I still don't know why, but this worked:

#include "SoftwareSerial.h"

// Constants
#define txPin 2      //tx pin in GPS connection
#define rxPin 3      //rx pin in GPS connection
char gps_data[200];

SoftwareSerial gps = SoftwareSerial(rxPin, txPin);
char byteGPS = 0;

void setup() {
  Serial.begin(9600);
  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);
  gps.begin(9600);
}

void loop() {

  byteGPS = '0'; // Remove data
  memset(gps_data, '\0', 200);

  while (gps.available())
  {
    while (gps.available())
    {
      byteGPS = gps.read(); // Read the char that is in the GPS serial port
      if (byteGPS == '$') break; 
    }
    int i=0;
    while (gps.available())  // Available3
    {
      delay(1);
      gps_data[i++] = byteGPS;
      byteGPS = gps.read();
      if ((byteGPS == '\n')) break;
    } 
    if (i > 1)
    {
      delay(1);
      parse_gps_data();
    } 
  } 
}

The code in parse_gps_data() parses the data from gps. It works fine.

  • 1
    Do the flush calls in setup not in loop, or you'll loose data, possible the $ you are looking for. – Code Gorilla Feb 3 '17 at 8:23
  • Do you mean "gps.flush()" and "Serial.flush()" ? I did, but I think this code has a bug and I can't understand very well, because when I reset the arduino I could see some numbers and strings ( those weren't correct actually) and It seems there is no loop!! – ReD Feb 3 '17 at 10:03
  • 1
    I mean both, but mainly gps.flush(). If you clear the input buffer that is receiving the GPS data every second or so its possible you will loose data. – Code Gorilla Feb 3 '17 at 13:19
  • I did it for both of them, but I don't know why I have to reset the arduino for receiving the data? – ReD Feb 3 '17 at 13:56
1

Your sketch has many problems:

  • The flush method waits for everything you have written to go out. You haven't written anything to the GPS or Serial, so there's no reason to flush the output. And you don't need to call it at the beginning of loop.

  • Calling delay is the surest way to lose GPS characters. Never use delay in a GPS sketch.

  • You are calling read without calling available first. Because you called delay, there are probably chars available, but not necessarily, and they will not be a complete sentence. This explains the partial NMEA data in your question.

  • You are printing too much in the plot routine. After 64 characters are printed (actually queued for printing), the Serial.print call must wait for some characters to be sent (making room in the output buffer). At 9600, each character takes 1ms to be sent. During that time, the GPS device is still sending characters. Eventually, the input buffer overflows and GPS characters are lost. When properly structured (see below), there is barely enough time to print all those messages.

  • You have connected the GPS to pins 1 & 2, but pins 0 & 1 are the HardwareSerial pins (Rx & Tx), dedicated to Serial . They are conflicting with each other, and may interfere with uploading new sketches via USB.

  • You do not have good satellite reception, according to the GPS data fragments you show. Check the antenna, get near a window, or go outside. It may take 10 minutes before a "fix" is acquired.

  • You are using SoftwareSerial for the GPS port. SoftwareSerial is very inefficient, because it disables interrupts for long periods of time. At 4800, the Arduino is dedicated to receiving each char for 2ms, an eternity for a 16MHz processor. This will interfere with other parts of the sketch or other libraries. This answer gives several alternatives, like AltSoftSerial, on different pins.

Solutions:

First, you have to get the hardware connections corrected. The NEO-6M is a 3.3V device, so you shouldn't connect it directly to the 5V Arduino. You could connect just the GPS TX to the Arduino, but you may not receive data reliably. Connecting an Arduino transmit pin to the GPS RX could damage the GPS module, as it is only rated for 3.6V MAX. It is best to "level-shift" between the two devices. You can use a few discrete parts (resistors + a diode, or resistors + FETs) or a level-shifting module. I recommend the latter, because they are cheap, and they also work for high-speed interfaces like SPI.

When you have level-shifting ready to add, move the GPS RX & TX connections to Arduino pin 9 (to GPS RX) and 8 (to GPS TX). This will let you use AltSoftSerial instead of SoftwareSerial. If you configure the NEO-6M to run at 9600 (that is the default), you could use NeoSWSerial on any two pins (besides 0 & 1 !!!). I'm not sure why your NEO-6M is running at 4800, unless you used the u-center utility to configure it permanently.

Finally, I would suggest trying my NeoGPS library. It is smaller, faster and more accurate than all other libraries, and it includes distance and bearing calculations that you might eventually need.

The Installation instructions guide you through several examples, after which you should try NMEAloc. NeoGPS also has a diagnostic program, and lots of tips on the Troubleshooting page. You can download it from the Arduino IDE Library Manager, under the menu Sketch -> Include Library -> Manage Libraries.

After going through those first examples, you should be ready to try your own sketch. Here is your sketch, modified to use NeoGPS and either AltSoftSerial, NeoSWSerial, or the built-in Serial1 on a Mega2560, Leo or Due:

// Set up the GPS serial port (pick *one* of these choices)

//#include <AltSoftSerial.h>
//AltSoftSerial gps_port; // always 8 & 9 on an UNO

//#include <NeoSWSerial.h>
//#define rxPin 2
//#define txPin 3
//NeoSWSerial gps_port( rxPin, txPin );

#define gps_port Serial1  // for a Mega2560, Leo or Due

// Variables
#include <NMEAGPS.h>
NMEAGPS gps;
gps_fix fix;

#ifndef NMEAGPS_PARSE_GGA
  #error You must uncomment NMEAGPS_PARSE_GGA in NMEAGPS_cfg.h!
#endif

void setup() {
  Serial.begin(9600);

  gps_port.begin(4800); // 9600 is the default for NEO-6M

  //setup satellites signal
  pinMode(13, OUTPUT);

  // Turn off the led until a satellite signal
  digitalWrite(13, LOW);

  Serial.println( F("Waiting GPS data...") );
}

void loop() {
  while (gps.available( gps_port )) {
    fix = gps.read();  // a new fix structure has been parsed
    plot();
  }
}

/*
  This function organize the gps data received for printing in the serial monitor.
*/

void plot() {

  if (fix.valid.location) {
  // or if (fix.valid.status && (fix.status >= gps_fix::STATUS_STD)) {

    digitalWrite(13, HIGH); // Then there are satellites, the LED switch ON

    Serial.println();
    Serial.println( F("----------------------------------------------") );
    Serial.print  ( F("UTC Hour -> ") );
    if (fix.valid.time)
      Serial.println( fix.dateTime.hours );
    Serial.print  ( F("Latitude -> ") );
    Serial.println( fix.latitude(), 6 );
    Serial.print  ( F("Longitude -> ") );
    Serial.println( fix.longitude(), 6 );
    Serial.print  ( F("GPS status: 0=NONE; 1=EST, 2=TIME, 3=STD, 4=DGPS -> ") );
    if (fix.valid.status)
      Serial.print( fix.status );
    Serial.println();
    Serial.print  ( F("Number of satellites -> ") );
    if (fix.valid.satellites)
      Serial.print( fix.satellites );
    Serial.println();

    #ifdef GPS_FIX_HDOP
      Serial.print  ( F("Horizontal Dilution of Precision (x1000) -> ") );
      if (fix.valid.hdop)
        Serial.print( fix.hdop );
      Serial.println();
    #else
      #error You must uncomment GPS_FIX_HDOP in GPSfix_cfg.h!
    #endif

    Serial.print  ( F("Antenna altitude (m) -> ") );
    if (fix.valid.altitude)
      Serial.print  ( fix.altitude(), 2 );
    Serial.println();

    #ifdef GPS_FIX_GEOID_HEIGHT
      Serial.print  ( F("Geoid Separation (m) -> ") );
      if (fix.valid.geoidHeight)
        Serial.print  ( fix.geoidHeight(), 2 );
      Serial.println();
    #else
      #error You must uncomment GPS_FIX_GEOID_HEIGHT in GPSfix_cfg.h!
    #endif

    Serial.println( F("----------------------------------------------") );
    Serial.println();

  } else {
    // If no satellite connection
    digitalWrite(13, LOW); // Turn off the LED

    Serial.println();
    Serial.println( F("-----------------------------") );
    Serial.print  ( F("|--- Status -->") );
    if (fix.valid.status)
      Serial.print  ( fix.status );
    Serial.println();
    Serial.println( F(" |") );
    Serial.println( F("|----Waiting location----|") );
    Serial.println( F("-----------------------------") );
    Serial.println();
  }
}

Note: Using #if statements, it verifies the configurations in NMEAGPS_cfg.h and GPSfix_cfg.h. The Geoid Height field is not enabled in the default NeoGPS configuration.

  • Since I am using Uno, is it ok to use Serial instead of Serial1? – ReD Feb 6 '17 at 9:14
  • This code causes an error error: ‘pgm_read_ptr’ was not declared in this scope which is a result of __AVR__ not being defined. Is there a work around to this? I tried defining it in GPSfix_cfg and NMEAGPS_cfg, but it didn't work. – ReD Feb 6 '17 at 9:26
  • 1
    What IDE version? 1.6.8 and 1.8.1 work fine for an UNO. 1.7.x did not compile, but it had a different error. I've updated NeoGPS_cfg.h to accommodate 1.7.x (again :-/). – slash-dev Feb 6 '17 at 15:12
  • 1
    Which IDE exactly? (1.6.x could be any of 13 IDEs.) What errors? If it is about "hdop", you have to enable HDOP in GPSfix_cfg.h, too. I've added that check to the above sketch. – slash-dev Feb 9 '17 at 13:28
  • 1
    So the NeoGPS program displays the GPS data in the Serial Monitor correctly? What do you mean by "pipe it to my program"? The NeoGPS program is reading gps_port correctly, parsing the characters, and filling out the fix structure. Then it prints the parsed values to Serial, but you could do whatever you want with fix.latitude() (for example). Your original program would never work, for the 7 reasons listed above. The -1 you get from gps.read() is reason #3. You only fixed reasons #1 and #2. – slash-dev Feb 12 '17 at 16:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.