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.

asked Feb 3, 2017 at 4:46
4
  • 1
    Do the flush calls in setup not in loop, or you'll loose data, possible the $ you are looking for. Commented Feb 3, 2017 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!! Commented Feb 3, 2017 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. Commented Feb 3, 2017 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? Commented Feb 3, 2017 at 13:56

1 Answer 1

2

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.

answered Feb 4, 2017 at 16:56
12
  • Since I am using Uno, is it ok to use Serial instead of Serial1? Commented Feb 6, 2017 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. Commented Feb 6, 2017 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 :-/). Commented Feb 6, 2017 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. Commented Feb 9, 2017 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. Commented Feb 12, 2017 at 16:48

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.