NEO6MV2 GPS shows first time received coordinates forever with SIM900A

Hello,

I am using NEO6Mv2 with SIM900A. As usual I am sending coordinates to my server using HTTP POST. HTTP post is working very well, no problem with it.

Problem is When I use it with SIM900A, it keeps on showing coordinates (that it received for first time) forever in loop().
However if I remove SIM900A code or simply make 84th line as

if(sats > 30)

in order to bypass the SIM900A related code, it works fine I mean it shows change in coordinates as I travel. (GPS cannot connect to 30 satellites at a time so it bypasses)

If you have these 2 modules, please try to use my sketch. In following code, SoftwareSerial ss is for GPS. I am also using TinyGPS library.

How to solve this problem ?

#include <SoftwareSerial.h>

#include <TinyGPS.h>

/* This sample code demonstrates the normal use of a TinyGPS object.
   It requires the use of SoftwareSerial, and assumes that you have a
   4800-baud serial GPS device hooked up on pins 4(rx) and 3(tx).
*/
#define RXPIN 4
#define TXPIN 5

TinyGPS gps;
SoftwareSerial ss(RXPIN, TXPIN);
SoftwareSerial SIM900(2,3);

static void smartdelay(unsigned long ms);
static void print_date(TinyGPS &gps);
static void print_str(const char *str, int len);
String strL, strN, message, textForSMS;
char charL[10], charN[10], text[200];
bool flag;

void setup()
{
  SIM900.begin(9600);
  ss.begin(9600);
  Serial.begin(9600);  
  ss.listen();
  
  Serial.print("Testing TinyGPS library v. "); Serial.println(TinyGPS::library_version());
  Serial.println("by Mikal Hart");
  Serial.println();
  Serial.println("Sats HDOP Latitude  Longitude  Fix  Date       Time     Date Alt    Course Speed Card  Distance Course Card  Chars Sentences Checksum");
  Serial.println("          (deg)     (deg)      Age                      Age  (m)    --- from GPS ----  ---- to London  ----  RX    RX        Fail");
  Serial.println("-------------------------------------------------------------------------------------------------------------------------------------");

  delay(2000);
  flag = true;
}

void loop()
{
  if(flag == false)
  {
    ss.listen();
    delay(2000);
  }
  
  int sats,hdops;
  float flat, flon;
  unsigned long age, date, time, chars = 0, llat, llon;
  unsigned short sentences = 0, failed = 0;
  //static const double LONDON_LAT = 51.508131, LONDON_LON = -0.128002;
  static const double LONDON_LAT = 19.105466, LONDON_LON = 72.849769;
  String message = "";

  memset(charL, 0, sizeof(charL));
  memset(charN, 0, sizeof(charN));

  
  
  sats = print_int(gps.satellites(), TinyGPS::GPS_INVALID_SATELLITES, 5);
  hdops = print_int(gps.hdop(), TinyGPS::GPS_INVALID_HDOP, 5);
  
  gps.f_get_position(&flat, &flon, &age);

  dtostrf(flat, 4, 6, charL);
  dtostrf(flon, 4, 6, charN);
  Serial.print(charL);
  Serial.print(" ");
  Serial.print(charN);

  message = "lat=";
  message += charL;
  message += "&lon=";
  message += charN;
  Serial.print(" ");
  Serial.println(message);
  Serial.println();

  Serial.print("Connected Sats ::: ");
  Serial.println(sats);
  Serial.println();
  if(sats > 0)
  {
    Serial.println(message);
    Serial.println();
    
    SIM900INIT(message);
    SIM900STOP();
    smartdelay(1000);
  }
  smartdelay(1000);
  flag = false;
}

static void smartdelay(unsigned long ms)
{
  unsigned long start = millis();
  do 
  {
    while (ss.available())
      gps.encode(ss.read());
  } while (millis() - start < ms);
}

float print_float(float val, float invalid, int len, int prec)
{
  if (val == invalid)
  {
    while (len-- > 1)
      Serial.print('*');
    Serial.print(' ');
  }
  else
  {
    Serial.print(val, prec);
    int vi = abs((int)val);
    int flen = prec + (val < 0.0 ? 2 : 1); // . and -
    flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
    for (int i=flen; i<len; ++i)
      Serial.print(' ');
  }
  smartdelay(0);
}

int print_int(unsigned long val, unsigned long invalid, int len)
{
  char sz[32];int returner;
  if (val == invalid)
    strcpy(sz, "*******");
  else
    sprintf(sz, "%ld", val);
  sz[len] = 0;
  for (int i=strlen(sz); i<len; ++i)
    sz[i] = ' ';
  if (len > 0) 
    sz[len-1] = ' ';
  returner = atoi(sz);
  return returner;

}

static void print_date(TinyGPS &gps)
{
  int year;
  byte month, day, hour, minute, second, hundredths;
  unsigned long age;
  gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age);
  if (age == TinyGPS::GPS_INVALID_AGE)
    Serial.print("********** ******** ");
  else
  {
    char sz[32];
    sprintf(sz, "%02d/%02d/%02d %02d:%02d:%02d ",
        month, day, year, hour, minute, second);
    Serial.print(sz);
  }
  print_int(age, TinyGPS::GPS_INVALID_AGE, 5);
  smartdelay(0);
}

static void print_str(const char *str, int len)
{
  int slen = strlen(str);
  for (int i=0; i<len; ++i)
    Serial.print(i<slen ? str[i] : ' ');
  smartdelay(0);
}

void SIM900INIT(String msg)
{
    
    SIM900.listen();
    String httpdata = "";

    
    SIM900.println("AT+SAPBR=3,1,\"Contype\",\"GPRS\"");
    delay(1000);

    SIM900.println("AT+SAPBR=3,1,\"APN\",\"bsnlnet\"");
    delay(1000);
    
    SIM900.println("AT+SAPBR=1,1");
    delay(3500);
    
    SIM900.println("AT+SAPBR=2,1");
    delay(3500); 
  
    SIM900.println("AT+HTTPTERM");
    delay(1000);
   
    SIM900.println("AT+HTTPINIT");
    delay(1000);
    
    SIM900.println("AT+HTTPPARA=\"CID\",1");
    delay(1000);
  
    SIM900.println("AT+HTTPPARA=\"URL\",\"hxxp://hostname.tld/record\"");
    delay(1000);
  
    SIM900.println("AT+HTTPPARA=\"CONTENT\",\"application/x-www-form-urlencoded\"");
    delay(1000);

    httpdata = "AT+HTTPDATA=";
    httpdata += msg.length();
    httpdata += ",2000";
    Serial.println(SIM900.println(httpdata));
    delay(1000);  
    
    Serial.println(SIM900.println(msg));
    delay(1000); 
    
    Serial.println(SIM900.println("AT+HTTPACTION=1"));
    delay(5000); 
}

void SIM900STOP()
{
  SIM900.println("AT+HTTPTERM");
  delay(1000);
}
void ShowSerialData()
{
  while(SIM900.available()!=0)
    Serial.write(SIM900.read());
}

I am really puzzled with this problem. :confused:

The main problem is that you are using delay(). The TinyGPS example program is quite fragile, and when combined with two SoftwareSerial ports and delays, it just isn't going to work.

A secondary problem will (eventually) be String. Using character arrays and the C-string routines like strcpy and strcat is very efficient. Also, they will not cause random hangs after hours or days, like String.

A third problem is using SoftwareSerial. It is very inefficient and disables interrupts for long periods of time. Instead, please try NeoSWSerial. If you could use pins 8 & 9, I would recommend AltSoftSerial for the GPS device. It is even better.

Here is a version of your sketch that uses NeoGPS, NeoSWSerial, and does not use String:

#include "NMEAGPS.h"
#include <NeoSWSerial.h>

#define RXPIN 4
#define TXPIN 5

NMEAGPS gps;
gps_fix fix;
NeoSWSerial gps_port(RXPIN, TXPIN);

// Uncomment one of these at a time.
NeoSWSerial SIM900(2,3);            // use this for really sending a message
//HardwareSerial & SIM900 = Serial; // use this for testing... SIM900 commands visible on Serial Monitor


void setup()
{
  Serial.begin( 9600 );  
  Serial.println( F("unixbox911 test") );

  SIM900  .begin( 9600 );
  gps_port.begin( 9600 );
}

void loop()
{
  while (gps.available( gps_port )) {
    fix = gps.read();
    doSomeWork( fix );
  }

} // loop

void doSomeWork( gps_fix & fix )
{
  if (fix.valid.location) {
    // Only do this when the GPS has a good lat/lon

    char message[40];
    strcpy_P( message, (const char *) F("lat=") );
    dtostrf( fix.latitude (), 4, 6, &message[ strlen(message) ] );
    strcat_P( message, (const char *) F("&lon=") );
    dtostrf( fix.longitude(), 4, 6, &message[ strlen(message) ] );

    Serial.println(message);
    Serial.println();

    Serial.print( F("Connected Sats ::: ") );
    if (fix.valid.satellites)
      Serial.println( fix.satellites );
    Serial.println();

    Serial.println(message);
    Serial.println();
      
    SIM900.listen();
    SIM900INIT(message);
    SIM900STOP();
    gps_port.listen();
  }
}
  

void SIM900INIT( char msg[] )
{
    SIM900.println( F("AT+SAPBR=3,1,\"Contype\",\"GPRS\"") );
    delay(1000);

    SIM900.println( F("AT+SAPBR=3,1,\"APN\",\"bsnlnet\"") );
    delay(1000);
    
    SIM900.println( F("AT+SAPBR=1,1") );
    delay(3500);
    
    SIM900.println( F("AT+SAPBR=2,1") );
    delay(3500); 
  
    SIM900.println( F("AT+HTTPTERM") );
    delay(1000);
   
    SIM900.println( F("AT+HTTPINIT") );
    delay(1000);
    
    SIM900.println( F("AT+HTTPPARA=\"CID\",1") );
    delay(1000);
  
    SIM900.println( F("AT+HTTPPARA=\"URL\",\"hxxp://hostname.tld/record\"") );
    delay(1000);
  
    SIM900.println( F("AT+HTTPPARA=\"CONTENT\",\"application/x-www-form-urlencoded\"") );
    delay(1000);

    SIM900.print  ( F( "AT+HTTPDATA=") );
    SIM900.print  ( strlen(msg) );
    SIM900.println( F(",2000") );
    delay(1000);  
    
    Serial.println( SIM900.println(msg) );
    delay(1000); 
    
    Serial.println(SIM900.println( F("AT+HTTPACTION=1") ));
    delay(5000); 
}

void SIM900STOP()
{
  SIM900.println("AT+HTTPTERM");
  delay(1000);
}

void ShowSerialData()
{
  while(SIM900.available()!=0)
    Serial.write(SIM900.read());
}

Your original sketch used 14,798 bytes of program storage space, and 1,316 bytes of RAM. This version uses only 11,104 bytes of program storage space, and 462 bytes of RAM.

I would also suggest reading the NeoGPS Troubleshooting section. It describes many common problems, including yours. The delays were preventing it from receiving the GPS data correctly: the input buffer was overflowing.

Cheers,
/dev

Hi /dev,

Thank you so much, I never heard about NeoSWSerial & NeoGPS. I will try this and let you know.

I am getting error while compiling.

In file included from C:\Users\abc\Documents\Arduino\libraries\NeoGPS-master/GPSfix.h:34:0,
                 from C:\Users\abc\Documents\Arduino\libraries\NeoGPS-master/NMEAGPS.h:28,
                 from neogps_neoswserial.ino:1:
C:\Users\abc\Documents\Arduino\libraries\NeoGPS-master/Location.h:57:52: error: 'NeoGPS::Location_t::RAD_PER_DEG' cannot appear in a constant-expression
     CONST_CLASS_DATA float DEG_PER_RAD     = 1.0 / RAD_PER_DEG;
                                                    ^
Error compiling.

I have posted this on their github issues page. Did you get this issue ?

From the Installation page:

You can extract/unzip the files for use in step 3, but DO NOT copy these files into your Arduino/Libraries folder! This is different from most libraries.

I think you just have to copy the .H and .CPP files from the Libraries/NeoGPS-master subdirectory into your sketch directory, then delete the Libraries/NeoGPS-master subdirectory. I just checked, and both NMEA.ino and the sketch I posted above compile correctly when the NeoGPS files are not in the Libararies subdirectory.

Cheers,
/dev

Hi /dev,

Still no luck.

I guess you have different version of this library. And the one which I downloaded might be unstable.

Can you please attach your Sketch ZIP file ?

Thanks

Hi,

After upgrading Arduino IDE to 1.6.11, this compiling problem has solved.

Your both the libraries are awesome, they work really great. Just a small query, how can I get number of connected Satellites and Time from your library ?

Thanks :slight_smile:

how can I get number of connected Satellites and Time from your library ?

From the Data Model page:

* fix.satellites, a satellite count
* fix.dateTime, a date/time structure (see Time.h), which contains year, month, day-of-month, hours, minutes, and seconds, accessed with

  • .* fix.dateTime.year,
  • .* fix.dateTime.month,
  • .* fix.dateTime.date,
  • .* fix.dateTime.hours, etc.
  • .* Time operations allow converting to and from total seconds offset from a de facto starting time (e.g., an epoch date/time "origin").
    * fix.dateTime_cs, in integer hundredths of a second

The sketch I posted above shows how to access the satellite count, on line 44. As the code demonstrates, you should always check the valid flag to make sure that a particular member really has data (i.e., it may be "empty" or "invalid").

Note that the Time structure contains both date and time elements. You can configure NeoGPS so that it only parses date, only parses time, or parses both. Streamers.cpp shows how to print the dateTime values with the streaming operator <<, here. That operator is implemented in Time.cpp, here. You could do the same thing:

   if (fix.valid.time) {
      Serial << fix.dateTime;
      Serial.println();
    }

If you don't need the date, or you don't like that format, just print the pieces you do want:

 if (fix.valid.time) {
    Serial.print( fix.dateTime.hours );
    Serial.print( ':' );
    if (fix.dateTime.minutes < 10)
      Serial.print( '0' ); // a leading zero for minutes like 04
    Serial.print( fix.dateTime.minutes );
  }

Cheers,
/dev

Thanks, I will run and update this thread.

Hi /dev,

Things are working very well except

fix.satellites

Most of the time it shows 0. And when I upload testing program from TinyGPS, it properly shows number of connected satellites.

Here is my code:

    char message[500];
    int sats = fix.satellites;
    int year = fix.dateTime.year;
    int month = fix.dateTime.month;
    int day = fix.dateTime.date;
    
    int hh = fix.dateTime.hours;
    int mm = fix.dateTime.minutes;
    int ss = fix.dateTime.seconds;
    
    strcpy_P( message, (const char *) F("gps_lat=") );
    dtostrf( fix.latitude (), 4, 6, &message[ strlen(message) ] );
    strcat_P( message, (const char *) F("&gps_lng=") );
    dtostrf( fix.longitude(), 4, 6, &message[ strlen(message) ] );

    strcat_P( message, (const char *) F("&sats=") );
    

//   ======================Attaching Sats==========================
    if( sats < 10 && sats > 0 )
    {
      dtostrf( sats, 1, 0, &message[ strlen(message) ] );
    }
    if( fix.satellites >= 10 )
    {
      dtostrf( sats, 2, 0, &message[ strlen(message) ] );
    }
    if( fix.satellites == 0 )
    {
      dtostrf( 0, 2, 0, &message[ strlen(message) ] );
    }

//    ======================Attaching Date==========================
    strcat_P( message, (const char *) F("&sattime=20") );
    if( year < 10 )
    {
      dtostrf( 0, 1, 0, &message[ strlen(message) ] );
    }
    dtostrf( year, 1, 0, &message[ strlen(message) ] );
    strcat_P( message, (const char *) F("-") );
    
    if( month < 10 )
    {
      dtostrf( 0, 1, 0, &message[ strlen(message) ] );
    }
    dtostrf( month, 1, 0, &message[ strlen(message) ] );
    strcat_P( message, (const char *) F("-") );
    
    if( day < 10 )
    {
      dtostrf( 0, 1, 0, &message[ strlen(message) ] );
    }
    dtostrf( day, 1, 0, &message[ strlen(message) ] );
    strcat_P( message, (const char *) F("T") );
    
//    ======================Attaching Time==========================
    if( hh < 10 )
    {
      dtostrf( 0, 1, 0, &message[ strlen(message) ] );
    }
    dtostrf( hh, 1, 0, &message[ strlen(message) ] );
    strcat_P( message, (const char *) F(":") );

    if( mm < 10 )
    {
      dtostrf( 0, 1, 0, &message[ strlen(message) ] );
    }
    dtostrf( mm, 1, 0, &message[ strlen(message) ] );
    strcat_P( message, (const char *) F(":") );

    if( ss < 10 )
    {
      dtostrf( 0, 1, 0, &message[ strlen(message) ] );
    }
    dtostrf( ss, 1, 0, &message[ strlen(message) ] );
    
    strcat_P( message, (const char *) F("Z") );


    Serial.print("Message ::: ");
    Serial.println(message);

Here I am generating a URL. It keeps on giving &sats= 0 all the time. There is a space between = and 0

However if I comment Attaching Date and Attaching Time, it properly shows the number of satellites. What is going wrong here ?

Well, since I can't see the whole program, I can only guess:

1) I don't know what Arduino you're using, but if it's an UNO, then

    char message[500];

... could be too big. According to that snippet, you would only need room for 76 characters (77 with the NUL terminator):

    gps_lat=-89.123456&gps_lng=-179.123456&sats=99&sattime=2016-12-31T23:59:59Z

... so this would be enough:

    char message[80];

Normally, I would have suggested just printing each piece instead of putting them all into a buffer. But since you need to know the message length before sending the message, you have to format the whole message to know how long it is.

Is there an AT command that lets you terminate the message with a CR/LF, a ^Z or an extra empty line? Then you could print out the pieces; you wouldn't need a message buffer in order to send the length first.

2) You are using dtostrf for integer values. Lat/lon are the only floating-point values, so you should use dtostrf for those values. All the other integers should be formatted with [

itoa

](avr-libc: <stdlib.h>: General utilities):

    char message[80];
    
//   ======================Attaching Location==========================
    strcpy_P( message, (const char *) F("gps_lat=") );
    if (fix.valid.location)
      dtostrf( fix.latitude (), 4, 6, &message[ strlen(message) ] );
    strcat_P( message, (const char *) F("&gps_lng=") );
    if (fix.valid.location)
      dtostrf( fix.longitude(), 4, 6, &message[ strlen(message) ] );

//   ======================Attaching Sats==========================
    strcat_P( message, (const char *) F("&sats=") );
    if (fix.valid.satellites)
      itoa( fix.satellites, &message[ strlen(message) ], 10 );

//    ======================Attaching Date & Time====================
    strcat_P( message, (const char *) F("&sattime=") );
    if (fix.valid.date && fix.valid.time) {
      itoa( fix.dateTime.full_year(), &message[ strlen(message) ], 10 );
      strcat_P( message, (const char *) F("-") );
      if (fix.dateTime.month < 10)
        strcat_P( message, (const char *) F("0") );
      itoa( fix.dateTime.month, &message[ strlen(message) ], 10 );
      strcat_P( message, (const char *) F("-") );
      if (fix.dateTime.date < 10)
        strcat_P( message, (const char *) F("0") );
      itoa( fix.dateTime.date, &message[ strlen(message) ], 10 );

      strcat_P( message, (const char *) F("T") );
      if (fix.dateTime.hours < 10)
        strcat_P( message, (const char *) F("0") );
      itoa( fix.dateTime.hours, &message[ strlen(message) ], 10 );
      strcat_P( message, (const char *) F(":") );
      if (fix.dateTime.minutes < 10)
        strcat_P( message, (const char *) F("0") );
      itoa( fix.dateTime.minutes, &message[ strlen(message) ], 10 );
      strcat_P( message, (const char *) F(":") );
      if (fix.dateTime.seconds < 10)
        strcat_P( message, (const char *) F("0") );
      itoa( fix.dateTime.seconds, &message[ strlen(message) ], 10 );
      strcat_P( message, (const char *) F("Z") );
    }

Note:

a) It checks the valid flag to make sure there is a value you can send. If no values are valid, the message would be:

    gps_lat=&gps_lng=&sats=&sattime=

Take out the "if (fix.valid.__)" tests if you really want to send values that may not be valid. Be careful, because that can be misleading to the receiver.

b) It uses the fix members instead of copying them to local integers.

c) It uses fix.dateTime.[u]date[/u] instead of day. I have clarified the documentation to say that day is the day-of-the-week. The date member is the day-of-the-month.

d) It adds leading zeroes if necessary to month, date, hours, minutes and seconds.

Cheers,
/dev

Hello Dev,

Tons of thanks first !
Yes I am using Uno, the reason for char message[500]; is that target URL is pretty long.
I was not aware about itoa() function.

Sorry but I am still facing the problem about number of satellites.

strcat_P( message, (const char *) F("&sats=") );
    if (fix.valid.satellites)
    {
      itoa( fix.satellites, &message[ strlen(message) ], 10 );
    }
    else
    {
      strcat_P( message, (const char *) F("0") );
    }

It keeps on showing sats=0 so it is a case fix.valid.satellites is failing ?

It keeps on showing sats=0 so it is a case fix.valid.satellites is failing ?

Yes, the satellites member is never getting set by a received sentences. You could have a configuration problem:

  1. Make sure that you have enabled at least one of the sentences that has a satellites field. According to the Choosing Your Configuration page, you must enable at least one of these sentences in NMEAGPS_cfg.h: GGA, GSA or GSV.

  2. Make sure that your device actually emits the sentence(s) that you have enabled. I would suggest using the NMEAorder.ino application. It will display all the sentences that are actually emitted by your GPS device.

  3. NMEAorder.ino will also display the LAST sentence the GPS devices sends in each 1-second interval. You must confirm that NMEAGPS_cfg.h uses the same sentence:

    #define LAST_SENTENCE_IN_INTERVAL NMEAGPS::NMEA_GSA
  1. Make sure that you have received at least two fixes after switching back to the GPS software serial port. If you use the first, fix, it may not have been populated with by a GSA sentence. The location may have been received by an RMC or GGA sentence. I STILL CAN'T SEE YOUR PROGRAM, but if it is like the previous version, do something like this:
void doSomeWork( gps_fix & fix )
{
  static uint8_t fixes = 0;
  
  fixes++;
  if ((fixes > 1) && fix.valid.location) {
      .
      .
      .
    SIM900.listen();
    SIM900INIT(message);
    SIM900STOP();
    gps_port.listen();

    fixes = 0;
  }
}

That will send a message after the second fix comes in. The second fix should be fully populated.

the reason for char message[500]; is that target URL is pretty long.

Then write it in two pieces:

const char url[] PROGMEM = "http://your.hostname.zz/index.php?op=foo..."; // saves RAM!

void SIM900INIT( char msg[] )
{
    SIM900.println( F("AT+SAPBR=3,1,\"Contype\",\"GPRS\"") );
    delay(1000);
       .
       .
       .
    SIM900.println( F("AT+HTTPPARA=\"CONTENT\",\"application/x-www-form-urlencoded\"") );
    delay(1000);

    size_t totalChars = strlen_P( url ) + strlen( msg ); // add the URL length to the msg length

    SIM900.print  ( F( "AT+HTTPDATA=") );
    SIM900.print  ( totalChars );
    SIM900.println( F(",2000") );
    delay(1000);  
    
    SIM900.print( (const __FlashStringHelper *) url ); // write the long url, from PROGMEM
    SIM900.println(msg);                    // write the paramaters, from a short char msg[80] RAM buffer
    delay(1000); 

    SIM900.println( F("AT+HTTPACTION=1") );
    delay(5000); 
}

This calculates the total length from the constant URL length (long) and the "formatted" message length (short).

Cheers,
/dev

Hi Dev,

I will try and update this thread. By the way sorry for late reply.

No worries.