neoGPS inside a class

Hi,

Ardunio newbee here. I am trying to use the neoGPS lib to parse NMEA from an Adafruit Ultimate GPS. I have this working with TinyGPS++ and the examples like NMEAloc.ino.

I want to use it within my own class. The class will wrap switching the GPS on / off and getting a fix for a main programme.

My class header file Gns.h has;

#include "NMEAGPS_cfg.h"
#include "GPSport.h"
#include <NMEAGPS.h>
class Gns
{
  public:
    Gns(byte pin);
    boolean on(void);
    void off(void);
    boolean isOn(void);
    unsigned long getInitialFix(unsigned long);
  private:
    int _powerPin;
    boolean _powerState;
    void setupGNS(void);
    NMEAGPS gps;
    gps_fix fix;

    boolean checkFix(const gps_fix & fix);
    
    boolean gnsFixTimeoutReached;
    unsigned long gnsFixTimeoutMs;
    unsigned long gnsTimeToFixMs;
    unsigned long now;
};

The Gns.cpp has the following method (amongst others);

unsigned long Gns::getInitialFix(unsigned long timeout) {
  gnsFixTimeoutReached = false;
  gnsFixTimeoutMs = millis() + timeout;
  gnsTimeToFixMs = 0;

  while ( !gnsFixTimeoutReached ) {
    // capture now
    now = millis();
  
    while (gps.available( gpsPort )) {
      // we should have a fix worth checking now
      DEBUGln("Gns::getInitialFix - gps.available( gpsPort )");
      checkFix( gps.read() );
    }
(snippage)
}

The issue I have is that gps.avaliable( gpsPort) never seems to return true, so that we never call checkFix( gps.read()).

The above example is almost identical to the the NMEAloc.ino example which has;

static void GPSloop()
{
  while (gps.available( gpsPort ))
    doSomeWork( gps.read() );

} // GPSloop

If I run the above on my hardware doSomeWork( gps.read() ); gets called.

Help?

What special thing do I need to do to run this within a class?

Many thanks.

The answer is ........ (snippage)

For a complete answer, post a full code that works and enough of the code that doesn't work to actually compile.

Okay - these compile;

ino file

#include "Gns.h"

#define D12_GPS_ENABLE 12
#define INITIAL_GPS_FIX_TIMEOUT_MSECS 1200000 // time to try and get a fix in msecs is 1200 secs, or 20 mins

Gns gns(D12_GPS_ENABLE);


void setup() {

  Serial.begin(9600);
  Serial.flush();
  Serial.println("Nooooooooo");
  
  gns.getInitialFix(INITIAL_GPS_FIX_TIMEOUT_MSECS);
  
}

void loop() {

}

Gns.h

#ifndef Gns_h
#define Gns_h

#include "NMEAGPS_cfg.h"
#include "GPSport.h"
#include <NMEAGPS.h>

#define DEBUG(input)   {Serial.print(input); Serial.flush();}
#define DEBUGln(input) {Serial.println(input); Serial.flush();}

class Gns
{
  public:
    Gns(byte pin);
    boolean on(void);
    void off(void);
    boolean isOn(void);
    unsigned long getInitialFix(unsigned long);
  private:
    int _powerPin;
    boolean _powerState;
    void setupGNS(void);
    NMEAGPS gps;
    gps_fix fix;

    boolean checkFix(const gps_fix & fix);
    
    boolean gnsFixTimeoutReached;
    unsigned long gnsFixTimeoutMs;
    unsigned long gnsTimeToFixMs;
    unsigned long now;
};

#endif

Gns.cpp

#include <Arduino.h>
#include "Gns.h"

Gns::Gns(byte pin) {
  
  // Constructor
  // Takes an int as pin to power up
  
  _powerPin = pin;
  pinMode(_powerPin, OUTPUT);
}

boolean Gns::on(void) {

  // switches on GNS
  // calls setup
  
  // Switch device on by putting pin HIGH
  digitalWrite(_powerPin, HIGH);
  _powerState = true;
  
  gpsPort.begin(9600);
  gpsPort.flush();
  Serial.println("GNS device on!");
  Serial.flush();
  setupGNS();
  if ( gpsPort.available() > 0) {
    // spitting something out so assume on!
    return true;
  } else {
    return false;
  }
}

void Gns::off(void) {

  // clears buffers and powers off GNS
  
  // turn off the Device by putting pin LOW
  digitalWrite(_powerPin, LOW);
  _powerState = false;

  // flush the port
  gpsPort.flush();

  Serial.println("GNS device off!");
}

boolean Gns::isOn(void) {
  
  // returns GNS power state
  // does not mean anything is actually working!
  
  // on == true
  // off == false
  //DEBUG("gps state is:");
  //DEBUGln(_powerState);
  return _powerState;
}

void Gns::setupGNS(void) {

  // Function to setup GNS
  // Sets BAUD, search for SBAS and enable WAAS/DGPS
  
  DEBUGln("Gns::setupGNS - begin") 
  // https://github.com/SlashDevin/NeoGPS/blob/master/examples/NMEArevGeoCache/NMEArevGeoCache.ino#L102
  // done with;
  // gpsPort.print
  //    ( F( "$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29\r\n" // RMC only...
  //       "$PMTK220,1000*1F\r\n" ) );    // ...and 1 Hz update rate
  // http://forums.adafruit.com/viewtopic.php?f=19&p=143502
  gpsPort.print( F("PMTK313,1*2E\r\n"  // Enable to search a SBAS satellite
    // https://code.google.com/p/ardupilot-mega/source/browse/libraries/AP_GPS/AP_GPS_NMEA.h?spec=svneff9f9d0906c1e3cd4e217b0363d0f9d44394e75&name=f401de6d52&r=eff9f9d0906c1e3cd4e217b0363d0f9d44394e75
    "$PMTK301,2*2E\r\n" // Enable WAAS as DGPS Source
    "$PMTK220,1000*1F\r\n" ) );    // ...and 1 Hz update rate
  

  // http://forums.adafruit.com/viewtopic.php?f=19&p=143502
  //DEBUGln("  Enable SBAS sat search");
  //Serial1.print("$PMTK313,1*2E\r\n");  // Enable to search a SBAS satellite
  //DEBUGln("  Enable WAAS as DPGS source");
  // http://aprs.gids.nl/nmea/#gga
  // report antenna
  //Serial1.print("$PGCMD,33,1*6C\r\n");

  DEBUG_PORT.begin(9600);
  DEBUG_PORT.flush();
  DEBUG_PORT.print( F("neogpspoc.INO: started\n") );
  DEBUG_PORT.print( F("fix object size = ") );
  DEBUG_PORT.println( sizeof(gps.fix()) );
  DEBUG_PORT.print( F("NMEAGPS object size = ") );
  DEBUG_PORT.println( sizeof(gps) );
  DEBUG_PORT.println( F("Looking for GPS device on " GPS_PORT_NAME) );
  DEBUG_PORT.flush();
  
  DEBUGln("Gns::setupGNS ... done")
}


unsigned long Gns::getInitialFix(unsigned long timeout) {

  // get an initial fix
  // takes a timeout
  // switches on GNS and loops until either
  // fix found or timeout reached
  // looks for a good fix if possible
  // returns gnsTimeToFixMs (which could be 0 for no fix)

  gnsFixTimeoutReached = false;
  gnsFixTimeoutMs = millis() + timeout;
  gnsTimeToFixMs = 0;
  //initialHDOP = 0;
  //finalHDOP = 0;
  //hdop = 0;

  DEBUG("Gns::getInitialFix - timeout: ");
  DEBUGln(timeout);

  // switch on GPS
  on();
  
  DEBUGln("Gns::getInitialFix - while: ");
  DEBUG_PORT.print( F("NMEAGPS object size = ") );
  DEBUG_PORT.println( sizeof(gps) );

  while ( !gnsFixTimeoutReached ) {
    // capture now
    now = millis();
    DEBUGln(".");

    //while (gps.available( gpsPort ))
      //doSomeWork( gps.read() );
  
    while (gps.available( gpsPort )) {
      // we should have a fix worth checking now
      DEBUGln("Gns::getInitialFix - gps.available( gpsPort )");
      checkFix( gps.read() );
    }

    if ( gnsFixTimeoutMs <= now ) {
      gnsFixTimeoutReached = true;
      break;
    }
    delay(250);
  }

  DEBUGln("We only got a reasonable fix !!!");
  DEBUG("gnsTimeToFixMs: ");
  DEBUGln(gnsTimeToFixMs);
  //printGPSData();
  off();
  return gnsTimeToFixMs;
  
  // done
}

boolean Gns::checkFix(const gps_fix & fix ) {

  DEBUGln("Gns::checkFix");

}

First, it would be very helpful to compare this version against your original (working, non-custom class) sketch. That's why I requested you post it.

One thing I see right away is that you shouldn't call pinMode() from the constructor. The hardware may not be completely set up yet because the constructor runs before certain (hidden) initialization routines run. Provide a begin() method that you call from setup().

The hardware may not be completely set up yet

No. The hardware setup function, init(), has NOT been called yet. So, the hardware IS DEFINITELY not setup.

Hi,

Thanks for the style tips - not seen them before.

However the hardware works perfectly in another sketch using tinygps++. The GPS powers on and outputs NMEA sentences on the Serial1.

This is a working example from the Library; NMEAloc.ino.

On github here;

#include "NMEAGPS_cfg.h"
#include "GPSport.h"
#include <NMEAGPS.h>

//======================================================================
//  Program: NMEAloc.ino
//
//  Description:  This program only parses an RMC sentence for the lat/lon.
//
//  Prerequisites:
//     1) NMEA.ino works with your device (correct TX/RX pins and baud rate)
//     2) The RMC sentence has been enabled.
//     3) Your device sends an RMC sentence (e.g., $GPRMC).
//
//  'Serial' is for debug output to the Serial Monitor window.
//
//  License:
//    Copyright (C) 2014-2017, SlashDevin
//
//    This file is part of NeoGPS
//
//    NeoGPS is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    NeoGPS is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with NeoGPS.  If not, see <http://www.gnu.org/licenses/>.
//
//======================================================================

//------------------------------------------------------------
// Check that the config files are set up properly

#if !defined( NMEAGPS_PARSE_RMC )
  #error You must uncomment NMEAGPS_PARSE_RMC in NMEAGPS_cfg.h!
#endif

#if !defined( GPS_FIX_TIME )
  #error You must uncomment GPS_FIX_TIME in GPSfix_cfg.h!
#endif

#if !defined( GPS_FIX_LOCATION )
  #error You must uncomment GPS_FIX_LOCATION in GPSfix_cfg.h!
#endif

#if !defined( GPS_FIX_SPEED )
  #error You must uncomment GPS_FIX_SPEED in GPSfix_cfg.h!
#endif

#if !defined( GPS_FIX_SATELLITES )
  #error You must uncomment GPS_FIX_SATELLITES in GPSfix_cfg.h!
#endif

#ifdef NMEAGPS_INTERRUPT_PROCESSING
  #error You must *NOT* define NMEAGPS_INTERRUPT_PROCESSING in NMEAGPS_cfg.h!
#endif

//------------------------------------------------------------

static NMEAGPS  gps; // This parses the GPS characters

//----------------------------------------------------------------
//  Print the 32-bit integer degrees *as if* they were high-precision floats

static void printL( Print & outs, int32_t degE7 );
static void printL( Print & outs, int32_t degE7 )
{
  // Extract and print negative sign
  if (degE7 < 0) {
    degE7 = -degE7;
    outs.print( '-' );
  }

  // Whole degrees
  int32_t deg = degE7 / 10000000L;
  outs.print( deg );
  outs.print( '.' );

  // Get fractional degrees
  degE7 -= deg*10000000L;

  // Print leading zeroes, if needed
  int32_t factor = 1000000L;
  while ((degE7 < factor) && (factor > 1L)){
    outs.print( '0' );
    factor /= 10L;
  }
  
  // Print fractional degrees
  outs.print( degE7 );
}

static void doSomeWork( const gps_fix & fix );
static void doSomeWork( const gps_fix & fix )
{
  //  This is the best place to do your time-consuming work, right after
  //     the RMC sentence was received.  If you do anything in "loop()",
  //     you could cause GPS characters to be lost, and you will not
  //     get a good lat/lon.
  //  For this example, we just print the lat/lon.  If you print too much,
  //     this routine will not get back to "loop()" in time to process
  //     the next set of GPS data.

  if (fix.valid.location) {

    if ( fix.dateTime.seconds < 10 )
      DEBUG_PORT.print( '0' );
    DEBUG_PORT.print( fix.dateTime.seconds );
    DEBUG_PORT.print( ',' );

    // DEBUG_PORT.print( fix.latitude(), 6 ); // floating-point display
    // DEBUG_PORT.print( fix.latitudeL() ); // integer display
    printL( DEBUG_PORT, fix.latitudeL() ); // prints int like a float
    DEBUG_PORT.print( ',' );
    // DEBUG_PORT.print( fix.longitude(), 6 ); // floating-point display
    // DEBUG_PORT.print( fix.longitudeL() );  // integer display
    printL( DEBUG_PORT, fix.longitudeL() ); // prints int like a float

    DEBUG_PORT.print( ',' );
    if (fix.valid.satellites)
      DEBUG_PORT.print( fix.satellites );

    DEBUG_PORT.print( ',' );
    DEBUG_PORT.print( fix.speed(), 6 );
    DEBUG_PORT.print( F(" kn = ") );
    DEBUG_PORT.print( fix.speed_mph(), 6 );
    DEBUG_PORT.print( F(" mph") );

  } else {
    // No valid location data yet!
    DEBUG_PORT.print( '?' );
  }

  DEBUG_PORT.println();

} // doSomeWork

//------------------------------------

static void GPSloop();
static void GPSloop()
{
  while (gps.available( gpsPort ))
    doSomeWork( gps.read() );

} // GPSloop
  
//--------------------------

void setup()
{
  DEBUG_PORT.begin(9600);
  while (!DEBUG_PORT)
    ;

  DEBUG_PORT.print( F("NMEAloc.INO: started\n") );
  DEBUG_PORT.print( F("fix object size = ") );
  DEBUG_PORT.println( sizeof(gps.fix()) );
  DEBUG_PORT.print( F("NMEAGPS object size = ") );
  DEBUG_PORT.println( sizeof(gps) );
  DEBUG_PORT.println( F("Looking for GPS device on " GPS_PORT_NAME) );

  #ifdef NMEAGPS_NO_MERGING
    DEBUG_PORT.println( F("Only displaying data from xxRMC sentences.\n  Other sentences may be parsed, but their data will not be displayed.") );
  #endif

  DEBUG_PORT.flush();

  gpsPort.begin(9600);
}

//--------------------------

void loop()
{
  GPSloop();
}

This is the section causing issues.

Is it a namespace problem because I have it within another class?

    while (gps.available( gpsPort )) {
      // we should have a fix worth checking now
      DEBUGln("Gns::getInitialFix - gps.available( gpsPort )");
      checkFix( gps.read() );
    }

This is the section causing issues.

What ARE those issues?

Apparently, you have not addressed the issues that have already been pointed out, or you would have posted the revise code (ALL of it).

One of your problems is definitely calling pinMode from the constructor. That may not be the only problem. But, until you fix that, test it, and post new results with complete and corrected code, I will not be looking further.

Hi,

Little confused. The arduino lib guide (Arduino - LibraryTutorial) has exactly the same constructor logic as mine.

Morse::Morse(int pin)
{
  pinMode(pin, OUTPUT);
  _pin = pin;
}

And just to clarify;

    while (gps.available( gpsPort )) {
      // we should have a fix worth checking now
      DEBUGln("Gns::getInitialFix - gps.available( gpsPort )");
      checkFix( gps.read() );
    }

My issue it that gps.avaliable (gpsPort) does not appear to return true in my class, so the while loop never runs, yet when I use the example NMEAloc with similar code, the exact same hardware it works fine.

This makes me assume its some sort of class/namespace issue.

I will move the pinMode code out of the constructor.

Little confused. The arduino lib guide (Arduino - LibraryTutorial) has exactly the same constructor logic as mine.

So what? Wrong is still wrong.

gregcope:
I will move the pinMode code out of the constructor.

Even better, during this debugging stage don't try to control GPS On/Off. Just hot wire the control to VCC so it's always on. Report results of doing this.

Also, this:

gnsFixTimeoutMs = millis() + timeout;

Is not the proper way to set / detect a timeout condition. Always use subtraction when working with millis() time and comparisons. Probably not the problem here, but it IS a problem.

It's not a "namespace" issue. If it were, you'd have lots of 'undefined' errors.