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 -1
s. 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 Answer 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 orSerial
, so there's no reason to flush the output. And you don't need to call it at the beginning ofloop
.Calling
delay
is the surest way to lose GPS characters. Never usedelay
in a GPS sketch.You are calling
read
without callingavailable
first. Because you calleddelay
, 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), theSerial.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, likeAltSoftSerial
, 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 ofSerial1
?Fazeleh– Fazeleh2017年02月06日 09:14:44 +00:00Commented 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.Fazeleh– Fazeleh2017年02月06日 09:26:46 +00:00Commented Feb 6, 2017 at 9:26 -
1What 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– slash-dev2017年02月06日 15:12:55 +00:00Commented Feb 6, 2017 at 15:12
-
1Which 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– slash-dev2017年02月09日 13:28:58 +00:00Commented Feb 9, 2017 at 13:28
-
1So 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 readinggps_port
correctly, parsing the characters, and filling out thefix
structure. Then it prints the parsed values toSerial
, but you could do whatever you want withfix.latitude()
(for example). Your original program would never work, for the 7 reasons listed above. The-1
you get fromgps.read()
is reason #3. You only fixed reasons #1 and #2.slash-dev– slash-dev2017年02月12日 16:48:38 +00:00Commented Feb 12, 2017 at 16:48
gps.flush()
. If you clear the input buffer that is receiving the GPS data every second or so its possible you will loose data.