PA6H/MT3339 GPS: how to know if time is GPS locked or from internal RTC?

Hi all

About two years ago I made a speed and time GPS reader using a PA6H (Global Top MT3339 chip). It is powered by a Li-ion battery. When it is off, the battery keeps the RTC of the GPS module working.

I've noticed that, if I power on the device after some days, the clock time is some seconds wrong. It's obvious, because internal RTC has a ±20ppm error. After some seconds (depending on signal strength) I see the time become correct, but the NMEA still reports 0 satellites in view. Only after another minute or so I read 1 or more satellites in use. Then, the chip can read time&date before the first satellite is locked.

Can I know when time is sinchronized before NMEA reports 1 satellite in view?

Thanks
Gianluca from Roma, Italy

That GPS device does not explicitly tell you that it using RTC, one GPS time signal or a complete multi-satellite fix. You might be able to detect when satellites start getting tracked from the GSV sentences.

Here is a NeoGPS sketch that is able to detect those three conditions:

#include <NMEAGPS.h>

NMEAGPS gps;
gps_fix fix;

#define gpsPort Serial1 // you may be using a different serial port

void setup()
{
  Serial.begin( 9600 );
  Serial.println( F("status,date/time,speed") );
  gpsPort.begin( 9600 );
}

void loop()
{
  if (gps.available( gpsPort )) {
    fix = gps.read();

    detectRTCtimeOnly();

    if (fix.valid.status) {
      
      if (fix.status < gps_fix::STATUS_TIME_ONLY) {
        Serial.print( F("RTC") );

      } else if (fix.status == gps_fix::STATUS_TIME_ONLY) {
        Serial.print( F("GPS") );

      } else {
        Serial.print( fix.status ); // 3 or above
      }
    }
    Serial.print( ',' );

    if (fix.valid.date || fix.valid.time) {
      Serial << fix.dateTime;
      Serial.print( '.' );
      if (fix.dateTime_cs < 10)
        Serial.print( '0' );
      Serial.print( fix.dateTime_cs );
    }
    Serial.print( ',' );
    
    if (fix.valid.speed)
      Serial.print( fix.speed_kph() );
    Serial.println();
  }
}

void detectRTCtimeOnly()
{
  if (fix.valid.status && (fix.status == gps_fix::STATUS_TIME_ONLY)) {
    // The MTK3339 will never send this value,
    //   but this is how you might detect it on ublox GPS devices.
    return;
  }

  bool noSatellites = not fix.valid.satellites or (fix.satellites == 0);
  bool haveTime     = fix.valid.date or fix.valid.time;
  bool tracking     = fix.valid.status and (fix.status >= gps_fix::STATUS_STD);

  if (not tracking) {
    // Look for tracked satellites from the GSV sentences
    for (uint8_t i=0; i < gps.sat_count; i++) {
      if (gps.satellites[ i ].tracked) {
        tracking = true;
        break;
      }
    }
  }

  if (haveTime and tracking and noSatellites) {
    // Force the fix status even though no NMEA sentences set it
    fix.valid.status = true;
    fix.status       = gps_fix::STATUS_TIME_ONLY;
  }

} // detectRTCtimeOnly

The function detectRTCtimeOnly looks at the number of satellites reported by the GGA sentence (0 at startup), whether a valid date/time was in the RMC & GGA sentences, and whether any satellites are being tracked by the GSV sentences.

This should display "RTC" when the internal RTC is being used. Then it should transition to "GPS" when at least one GPS satellite is being tracked. Finally, it should display "3" or "4" when a complete fix is received (with location and speed). Here is some sample output:

status,date/time,speed
RTC,2018-04-06 14:14:04.31,
RTC,2018-04-06 14:14:05.00,
GPS,2018-04-06 14:14:06.00,
GPS,2018-04-06 14:14:07.00,
GPS,2018-04-06 14:14:08.00,
GPS,2018-04-06 14:14:09.00,
GPS,2018-04-06 14:14:10.00,
GPS,2018-04-06 14:14:11.00,
GPS,2018-04-06 14:14:12.00,
GPS,2018-04-06 14:14:13.00,
GPS,2018-04-06 14:14:14.00,
GPS,2018-04-06 14:14:15.00,
GPS,2018-04-06 14:14:16.00,
RTC,2018-04-06 14:14:17.00,
GPS,2018-04-06 14:14:18.00,
GPS,2018-04-06 14:14:19.00,
RTC,2018-04-06 14:14:20.00,
RTC,2018-04-06 14:14:21.00,
RTC,2018-04-06 14:14:22.00,
RTC,2018-04-06 14:14:23.00,
RTC,2018-04-06 14:14:24.00,
3,2018-04-06 14:14:25.00,3.48
3,2018-04-06 14:14:26.00,2.03
3,2018-04-06 14:14:27.00,2.26
3,2018-04-06 14:14:28.00,4.40
3,2018-04-06 14:14:29.00,1.14

This would not be nearly as easy with other GPS libraries.

If you want to try it on your device, NeoGPS is available from the Arduino Library Manager, under the menu Sketch-> Include Library-> Manage Libraries. NeoGPS is smaller, faster, more reliable and more accurate than all other libraries. You will have to enable these items in NMEAGPS_CFG.h:

#define NMEAGPS_PARSE_GSV

#define NMEAGPS_PARSE_SATELLITES
#define NMEAGPS_PARSE_SATELLITE_INFO

Cheers,
/dev

Ooops... I forgot to say I use Adafruit_GPS.h in my project.

You are saying that time is synchronized if I read 1 satellite or more, that's right?...

If I power on the device indoor, GPS signals are weak, then often I see 0 satellites, but after a few seconds the time is synchronized anyway, because the GPS module is only able to read time & date, but not all data. If it would be able to read all basic data, it would show 1 satellite or more... :slight_smile:

Ooops... I forgot to say I use Adafruit_GPS.h in my project.

As I said... "This would not be nearly as easy with other GPS libraries."

Actually, it cannot be done with Adafruit_GPS. You cannot "read all basic data" with Adafruit_GPS.
It doesn't parse GSV sentences, nor does it verify the checksum.

If you post your code...

Here it is:

IDE 1.8.5:
Lo sketch usa 30592 byte (94%) dello spazio disponibile per i programmi. Il massimo è 32256 byte.
Le variabili globali usano 1411 byte (68%) di memoria dinamica, lasciando altri 637 byte liberi per le variabili locali

In the first tab, you can read an explanation of variables (I prefer using short names, because my brain is very efficient, but it has a little video RAM :slight_smile: ).

a_commenti: there is programming history, hardware configuration and EEPROM locations.

b_simboli: icons definition.

c_Setup: setup: pin modes and EEPROM loading (default values if it is blank).

c_Loop: loop:

  • Refresh battery icon every 30 seconds
  • Verify power switch every 500mS. If power switch is off, backups then power off Mosfet.
  • Read the button: at least 1200mS pressed: jump to Menu1; Menu1: after 1300mS more, jump to Menu2. If short pressed, recall limit presets in sequence.
  • If I rotate the encoder, I change speed limit by 10km/h steps.
  • Every 500mS, if newNMEAreceived && parsed, read satellites.
  • If Fix, then print "F".
  • Read, format and print speed.
  • If Acquisition is pressed, set current speed as limit.
  • Refresh LEDs state
  • Switch A2 for loop speed measurement.

d_Enc_CalcLim:
encoder: read encoder, returning 1 (CW) or 0 (no action) or -1 (CCW).
CalcoloLimiti: read limits table and calculate pre-limit L1 and ultra limit MAX.

e_Menu1:

  • Shut down LEDs, because they can't be updated.
  • Read and print Time & date.
  • Read and print coordinates and distance from zero point.
  • Read and print battery voltage.

e_Menu2:

  • Shut down LEDs, because they can't be updated.
  • Contrast setting.
  • Speed preset setting.
  • Bip setting: when is it allowed?
  • Power on limit setting: the last or a fixed one?

f_Taratura:

  • How many speed presets?
  • Battery capacity setting by voltage decrease speed (mV/h).
  • Direction arrow setting: like a compass or upper North?

h_Bip_LED:

  • Bip.
  • Short Bip.
  • R, G, B LEDs driving.

i_LCDmask: print LCDmask after a LCD.clear.

j_OraeData:

  • Read and print Altitude. If NoFix, clear Altitude from LCD.
  • Apply DST.
  • Format time and date for printing.
  • Show satellites count.

k_Coordinate_Dir:

  • Decimal coordinates conversion.
  • Print simple air distance or real traveled distance (by steps; experimental).
  • Format Latitude and Longitude.
  • Trigonometric distance calculation.
  • GPS.angle to 8 direction arrow icons.

l_OraLegale: Calculate DST Yes/No for each hour since 2015 to 2050.

m_Batteria:

  • Read voltage and print Battery icon.
  • Measure battery voltage and estimate residual working time.

n_LEDFixNoFix:
If no fix:

  • Pulsing Red LED if 0 satellites.
  • Pulsing Yellow LED if 1 or more satellites.
    If fix:
  • Power off Red & Yellow LEDs.

Nice!

I see that it is using almost all the program space (94%) and RAM (68%). I think you can save quite a bit with some common techniques and by using NeoGPS. I have attached the NeoGPS version.

Major changes:

  • lcd.print("xxxx") ==> lcd.print( F("xxxx") )

  • lcd.print(".") ==> lcd.print( '.' )

  • Fixed width fields are printed differently. Instead of

    if (val < 10) lcd.print( "  " + val );
    else if (val < 100) lcd.print( " " + val );
    else lcd.print( val );

It does this:

    if (val < 10)
      lcd.print( ' ' );
    if (val < 100)
      lcd.print( ' ' );
    lcd.print( val );
  • Instead of one GPS variable, there are two: GPS (the parser) and fix (the GPS data fields)

  • It uses NeoSWSerial instead of SoftwareSerial

  • The String class is not used any more. Prints like this

    lcd.print( String(val) + "    " );

...are now separate pieces:

    lcd.print( val );
    lcd.print( F("    ") );

This saves about 1500 bytes of program space and makes your program more reliable.

The NeoGPS version uses 25860 bytes of program space (80%) and 773 bytes of RAM (35%), a significant savings.

Unfortunately, I cannot do much testing. If you're willing to try it, I can certainly help with any specific problems you find.

I did not try to add in the GSV parsing to solve your original question. I would suggest making sure that this works, first. When it's ok, you could update/create a github branch. I can help add the RTC/GPS time detection to that version.

Cheers,
/dev

Limiti.zip (24.4 KB)

Hi, /dev.
Thank you for your work. I've tried installing the two libraries and compiling, but IDE 1.8.5 doesn't work...:

Arduino:1.8.5 (Windows 10), Scheda:"Arduino Uno EEPROM SAVE"

Archiving built core (caching) in: C:\Users\Gianluca\AppData\Local\Temp\arduino_cache_761423\core\core_arduino_avr_unoEESAVE_4c41a3926b0be113a1229fc6680543f0.a
libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::storeFix()'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::string_for(NMEAGPS::nmea_msg_t) const'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseDDMMYY(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseFix(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseNS(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseEW(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::NMEAGPS()'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::NMEAGPS()'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::sentenceBegin()'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::sentenceInvalid()'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::sentenceUnrecognized()'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::headerReceived()'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseCommand(NMEAGPS::msg_table_t const*, unsigned char, char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseCommand(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::nmea_msg_table'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseGLL(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseGSA(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseGST(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseGSV(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseVTG(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseZDA(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseTime(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseFloat(gps_fix::whole_frac&, char, unsigned char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseFloat(unsigned int&, char, unsigned char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseDDDMM(long&, char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseLat(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseLon(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseSpeed(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseHeading(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseRMC(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseAlt(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseGeoidHeight(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseSatellites(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseGGA(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseField(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here
libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::sentenceOk()'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::decode(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::handle(unsigned char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseHDOP(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parseVDOP(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parsePDOP(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parse_lat_err(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parse_lon_err(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::parse_alt_err(char)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::read()'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::send(Stream*, char const*)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::send_P(Stream*, __FlashStringHelper const*)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoGPS-master\NMEAGPS.cpp.o (symbol from plugin): In function `NMEAGPS::storeFix()':

(.text+0x0): multiple definition of `NMEAGPS::poll(Stream*, NMEAGPS::nmea_msg_t)'

sketch\NMEAGPS.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoSWSerial-master\NeoSWSerial.cpp.o (symbol from plugin): In function `NeoSWSerial::read()':

(.text+0x0): multiple definition of `NeoSWSerial::read()'

sketch\NeoSWSerial.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoSWSerial-master\NeoSWSerial.cpp.o (symbol from plugin): In function `NeoSWSerial::read()':

(.text+0x0): multiple definition of `NeoSWSerial::rxChar(unsigned char)'

sketch\NeoSWSerial.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoSWSerial-master\NeoSWSerial.cpp.o (symbol from plugin): In function `NeoSWSerial::read()':

(.text+0x0): multiple definition of `NeoSWSerial::ignore()'

sketch\NeoSWSerial.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoSWSerial-master\NeoSWSerial.cpp.o (symbol from plugin): In function `NeoSWSerial::read()':

(.text+0x0): multiple definition of `NeoSWSerial::listen()'

sketch\NeoSWSerial.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoSWSerial-master\NeoSWSerial.cpp.o (symbol from plugin): In function `NeoSWSerial::read()':

(.text+0x0): multiple definition of `NeoSWSerial::setBaudRate(unsigned int)'

sketch\NeoSWSerial.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoSWSerial-master\NeoSWSerial.cpp.o (symbol from plugin): In function `NeoSWSerial::read()':

(.text+0x0): multiple definition of `NeoSWSerial::begin(unsigned int)'

sketch\NeoSWSerial.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoSWSerial-master\NeoSWSerial.cpp.o (symbol from plugin): In function `NeoSWSerial::read()':

(.text+0x0): multiple definition of `NeoSWSerial::attachInterrupt(void (*)(unsigned char))'

sketch\NeoSWSerial.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoSWSerial-master\NeoSWSerial.cpp.o (symbol from plugin): In function `NeoSWSerial::read()':

(.text+0x0): multiple definition of `NeoSWSerial::startChar()'

sketch\NeoSWSerial.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoSWSerial-master\NeoSWSerial.cpp.o (symbol from plugin): In function `NeoSWSerial::read()':

(.text+0x0): multiple definition of `NeoSWSerial::rxISR(unsigned char)'

sketch\NeoSWSerial.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoSWSerial-master\NeoSWSerial.cpp.o (symbol from plugin): In function `NeoSWSerial::read()':

(.text+0x0): multiple definition of `NeoSWSerial::checkRxTime()'

sketch\NeoSWSerial.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoSWSerial-master\NeoSWSerial.cpp.o (symbol from plugin): In function `NeoSWSerial::read()':

(.text+0x0): multiple definition of `NeoSWSerial::available()'

sketch\NeoSWSerial.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoSWSerial-master\NeoSWSerial.cpp.o (symbol from plugin): In function `NeoSWSerial::read()':

(.text+0x0): multiple definition of `NeoSWSerial::write(unsigned char)'

sketch\NeoSWSerial.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoSWSerial-master\NeoSWSerial.cpp.o (symbol from plugin): In function `NeoSWSerial::read()':

(.text+0x0): multiple definition of `__vector_3'

sketch\NeoSWSerial.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoSWSerial-master\NeoSWSerial.cpp.o (symbol from plugin): In function `NeoSWSerial::read()':

(.text+0x0): multiple definition of `__vector_4'

sketch\NeoSWSerial.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\NeoSWSerial-master\NeoSWSerial.cpp.o (symbol from plugin): In function `NeoSWSerial::read()':

(.text+0x0): multiple definition of `__vector_5'

sketch\NeoSWSerial.cpp.o (symbol from plugin):(.text+0x0): first defined here

collect2.exe: error: ld returned 1 exit status

exit status 1
Errore durante la compilazione per la scheda Arduino Uno EEPROM SAVE.

Questo report potrebbe essere più ricco di informazioni abilitando l'opzione
"Mostra un output dettagliato durante la compilazione"
in "File -> Impostazioni"

It looks like you did not use the Library Manager to install NeoGPS. Delete all NeoGPS files from your sketch directory.

You're right. At first, I copied some NeoGPS files beside the sketch.

The String class is not used any more. Prints like this
lcd.print( String(val) + " " );

...are now separate pieces:
lcd.print( val );
lcd.print( F(" ") );

I used String because I think a lcd.print(String...) is faster than two lcd.print, because writing to LCD is a slow operation.

Thank you again for your work, /dev!

[Upload files · Gatware/Limiti · GitHub]](Upload files · Gatware/Limiti · GitHub)

System is running. I'll test outdoor if GPS is ok.
At this moment I have had to do 3 little corrections: 2 were for a wrong space and a line before (or after...) where it had to be; another problem was because I have set TX on A4, but I use A4 for reading charging status, then the plug icon was ever drawn. I don't know exactly why, before, it didn't cause conflicts, but it should have been the same...

In e_Menu1 you declare:
extern void Coordinate();
extern void BattVolt();

I didn't do it, but it was working...?

What is the difference using print(' ') instead of print(" ")?

How can I know if time is valid? Can I simpy use else if (fix.valid.time) lcd.print('t'); ?

void OraeData()
{
  if (GPS.available( gpsSerial ))
    {
    fix = GPS.read();

    lcd.setCursor(15,0); 
    if (fix.valid.status && (fix.status >= gps_fix::STATUS_STD))
      lcd.print('F'); // Se ha fatto il punto scrive F
    else if (fix.valid.time) lcd.print('t');
    else lcd.print( ' ' ); // NoFix

Thanks!

... separate pieces:
lcd.print( val );
lcd.print( F(" ") );

I used String because I think a lcd.print(String...) is faster

Actually, the same number of characters get written to the LCD with either technique. The transfer time is the same.

However, constructing a String object and concatenating it with another piece actually takes much longer than calling print twice.

In e_Menu1 you declare:

    extern void Coordinate();

extern void BattVolt();



I didn't do it, but it was working...?

Yes, when you have many INO files in the sketch directory, the IDE concatenates them into one big file. Sometimes, the order of that concatenation causes an error like "Coordinate not declared." Those simple "extern" statements are called "forward declarations".

I added a routine, DisplayLimiti, and it caused several of these errors. I tried removing them, and it compiles ok. You can try removing any or all of them.

You may eventually want to have one INO file (Limiti.ino) and multiple H (declarations) and CPP (executable code) files. Multiple INO files are ok.

What is the difference using print(' ') instead of print(" ")?

The single apostrophe is for one character literals or constants, like one space character ' '. This is the char type.

The double quote is for multiple-character strings, like "ABC". This is actually an array, char [], also called "C strings". This is not the [u]S[/u]tring class.

When you use a double-quoted string, the compiler makes a "secret" array out of it, and also puts a zero byte after the last character. This is called the "NUL terminator".

Any routine that receives a C string (like print) needs to know the length of this string. In the case of lcd.print, it prints each character in the array, until it sees a zero character. Then it stops.

So when you write this code:

    lcd.print( "ABC" );

The compiler generates this for you:

    char secretArray[ 4 ] = { 65, 66, 67, 0 };
    lcd.print( secretArray );

When you print a single character, there is no extra zero byte, and the print routine does not loop through multiple characters.

The single character print is a little faster and uses a little less program space than the double-quoted string print. It's not a big difference, but it can help when there are a lot of them, and you're short on space.

I have had to do 3 little corrections

Sorry, I am glad you were able to find them easily!

the plug icon was never drawn.

There was one place that I replaced a write(byte(0)) with a print(' '). I thought you were trying to print the NUL terminator. My mistake. Check line 13 of j_orae_data.INO, I think it should be:

    lcd.write( byte(0) );

Also, I would suggest giving names to these special characters in Limiti.ino:

const byte NO_FIX_ICON  = 0;
const byte DEGREE_ICON  = 1;
const byte CHARGE_ICON  = 2;
const byte DIR_ICON     = 3;
const byte BATTERY_ICON = 4;

Then it's obvious what is being printed:

    lcd.write( NO_FIX_ICON );

One last thing... I was not sure about the lat/lon display format you wanted. I only displayed the degrees format, like 41.890178, 12.492274. These are floating-point degrees, not Degrees-Minutes-Seconds. They are positive for N and E and negative for S and W. I did not display the DEGREE_ICON.

Did you also want a DMS display? Some paper maps have DMS grids.

The DDMM.mmmm format is not very useful, are you sure you want that? It is the raw NMEA format, but it is not used on any maps that I have seen.

Also, I changed some delay calls to a while loop. This keeps the GPS fix updated and avoids losing any GPS characters:

  while ((millis() - t1 < 500) || (digitalRead(10)==LOW))
    {
    if (GPS.available( gpsSerial ))
      fix = GPS.read();
    }

You may want to make a new routine that can be called from all of these places:

bool checkGPS()
{
  bool newData = GPS.available( GPSserial );

  if (newData)
    fix = GPS.read(); // get the latest

  return newData;
}

Then you can use it everywhere, like this:

  while ((millis() - t1 < 500) || (digitalRead(10)==LOW))
    {
    checkGPS();
    }

Cheers,
/dev

How can I know if time is valid? Can I simply use else if (fix.valid.time) lcd.print('t'); ?

Yes!

There is also a fix.valid.date, if you need that. Both the date and the time are stored in fix.dateTime. If the date is not valid, it will be the initial value: 01-01-2000. If the time is not valid, it will be the initial value, 00:00:00.

You may have noticed that the OraeData() function checks both:

   if (fix.valid.date && fix.valid.time) {
      OraLegale();

That's because you need both to calculate the local time from the UTC time, and detect Daylight Saving Time from the date.

It has been my second Arduino project, then I've made some functions only for learning. Some choices have been not fully motivated. If I'm right, lat & lon were the same format of Google maps or the same of an Android application...

Before it, I've done a beautiful XLR cable tester. It is different from all others, because it has a LED for each status: OK, out of phase (could be intentional), unbalanced (could be intentional), short circuit (wrong!), hot wire interrupted, cold wire interrupted, shield interrupted, cases to ground. It works very well and it have been very useful for testing a stagebox.

After it, I've done a Geiger counter. It doesn't count only in a fixed time, but it shows second by second the estimated final result and the statistically estimated error.

Future projects are:

  • Spectrometer (for light). It involves optics, in which I have no experience. I have had some help from a friend of mine, working in ENEA. I've already bought diffraction grating and the two lenses.
  • Frequency meter using a 50MHz TTL 2x16-bit counter.

lat & lon were the same format of Google maps or the same of an Android application.

Yes, that's the simple floating-point degrees: fix.latitude() and fix.longitude().

It's strange the fact that "t" appears immediately when I power on, also in the storage room, where no satellites could be received... It seems if (fix.valid.time) be true also when no satellites are received...

Uh... I'm sorry!
"At this moment I have had to do ONLY 3 little corrections" :slight_smile:

It's strange the fact that "t" appears immediately when I power on... It seems if (fix.valid.time) be true also when no satellites are received..

Ha, I think we are back to your original question. The time is valid, because the GPS has an internal RTC.

If everything else is working, update github and I'll suggest where the RTC/GPS detection code should go. You'll also have to change some NeoGPS config files and the PMTK configuration commands in setup.

Done :slight_smile:

First, look at Reply 1. Add the detectRTCtimeonly routine to one of your INO files, and enable those three configuration items in NMEAGPS_cfg.h.

Second, consolidate all the GPS.available/GPS.read sections into one routine:

bool checkGPS()
{
bool newData = GPS.available( gpsSerial );
if (newData)
  {
  // Once per second, new GPS information is ready.
  fix = GPS.read();

  detectRTCtimeOnly();
  }
  
return newData;
}

Then call it from all the places that check for GPS data: c_Loop, e_Menu1, j_OraeData and k_Coordinate_Dir. For example, in c_Loop, change this:

// Per il GPS: (Sembra che non serva... Senza, funziona lo stesso!)
if (GPS.available( gpsSerial ))
  {
  // Once per second, new GPS information is ready.
  fix = GPS.read();

  lcd.setCursor(14,1);
  if (fix.valid.satellites)
     ...

... to this:

// Per il GPS: (Sembra che non serva... Senza, funziona lo stesso!)
if (checkGPS()) {

  lcd.setCursor(14,1);
  if (fix.valid.satellites)
     ...

And in e_Menu1, change this:

bool esce_da_menu1 = false;
while((millis()-t1<500) || (digitalRead(10)==LOW)) // Attende che venga lasciato il pulsante.
  {
  if (GPS.available( gpsSerial ))
    fix = GPS.read();

  if(millis()-t1>1000)
    {

... to this:

bool esce_da_menu1 = false;
while((millis()-t1<500) || (digitalRead(10)==LOW)) // Attende che venga lasciato il pulsante.
  {
  checkGPS();

  if(millis()-t1>1000)
    {

At this point your program should build. It will run, but your GPS device won't emit GSV sentences until you change this line:

  GPS.send_P( &gpsSerial, F("PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0") );

... to this:

  GPS.send_P( &gpsSerial, F("PMTK314,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0") );

Then you have to decide what to display:

   else if (fix.valid.time)
    {
    // Legge solo ora e data
    char timeStatus;
    if (fix.valid.status) {
      if (fix.status == gps_fix::STATUS_NONE)
        timeStatus = '-'; // internal RTC
      else if (fix.status == gps_fix::STATUS_TIME_ONLY)
        timeStatus = 't'; // at least one GPS satellite
      else
        timeStatus = 'T'; // multiple satellites
    } else {
        timeStatus = '?'; // no status value yet (could happen at power-up)
    }
    lcd.print( timeStatus );
    lcd.setCursor(9,0);
    lcd.print(F(" --  "));
    V = 0;
    }