SlashDevin / NeoGPS

NMEA and ublox GPS parser for Arduino, configurable to use as few as 10 bytes of RAM
GNU General Public License v3.0
695 stars 193 forks source link

Interface for non-stream GPS data #70

Closed theincredibleschnigges closed 6 years ago

theincredibleschnigges commented 6 years ago

I am working with a SIM808 module which combines GSM and GPS processing. Retrieving data from the module works by sending an AT command and awaiting the response. There is no continuous stream of data over the serial port.

Is there a method that takes e.g. a char* containing a parsed NMEA message (see https://www.elecrow.com/download/SIM800%20Series_GNSS_Application%20Note%20V1.00.pdf page 10) instead of reading from serial port using available(...)?

I just saw that I apparently can also configure the module the send the GPS data over the serial port using AT+CGNSTST (see beginning of page 8 of the pdf) - but I have no idea how that interferes with the GSM communication - and neither does the library I am using (TinyGSM) support that :(

I would appreciate it very much if anyone had an idea how to make this setup (SIM808/TinyGSM with NeoGPS) work.

SlashDevin commented 6 years ago

As mentioned here, to take the "parsed" NMEA values from a comma-separated response string, do something like this:

    // After final CR/LF received in char array "receivedChars":

    char *field = strtok( receivedChars, "," ); // first field is GPS run status
    field = strtok( nullptr, "," ); // FIX status
    field = strtok( nullptr, "," ); // UTC date/time
    field = strtok( nullptr, "," ); // Lat
    float lat =atof( field );
    field = strtok( nullptr, "," ); // Lon
    float lon = atof( field );
    field = strtok( nullptr, "," ); // Alt
    float alt = atof( field );
    field = strtok( nullptr, "," ); // Speed
    field = strtok( nullptr, "," ); // Course
    field = strtok( nullptr, "," ); // Fix Mode (0 = none, 1 = standard, 2 = differential)
    int fixMode = atoi( field );
    field = strtok( nullptr, "," ); // Reserved
    field = strtok( nullptr, "," ); // HDOP
    float hdop = atof( field );
    field = strtok( nullptr, "," ); // PDOP
    field = strtok( nullptr, "," ); // VDOP
    field = strtok( nullptr, "," ); // Reserved
    field = strtok( nullptr, "," ); // GPS satellites in view
    field = strtok( nullptr, "," ); // GNSS satellites in view
    int satellites = atoi( field );

    Serial.print( fixMode );
    Serial.print( ',' );
    Serial.print( hdop );
    Serial.print( ',' );
    Serial.print( satellites );
    Serial.print( ',' );

    if ((fixMode >= 1) && (hdop <= 2.0) && (satellites >= 4)) {
      // lat and lon are probably ok...
      Serial.print( lat );
      Serial.print( ',' );
      Serial.print( lon );
    }
    Serial.println();

Different devices have different field orders, so check them carefully.

To process the raw NMEA characters from the SIM800 stream (after putting it in that mode), you can do something like this:

  switch (state) {

    case WAITING:
      serialClear();
      if (millis() - stateTime >= 10000) // once every 10 seconds
        state = GPS_PWR_ON;
      break;

    case GPS_PWR_ON:
      //DEBUG("GPS PWR ON");
      SIMCOM.println( F("AT+CGPSPWR=1") );
      state     = GPS_PWR_ON_DELAY;
      stateTime = millis();
      break;

    case GPS_PWR_ON_DELAY:
      serialClear();
      if (millis() - stateTime >= 200)
        state = GPS_PKT_ON;
      break;

    case GPS_PKT_ON:
      //DEBUG("GPS PKT ON");
      SIMCOM.println( F("AT+CGPSOUT=1") );
      state     = GPS_PKT_ON_DELAY;
      stateTime = millis();
      break;

    case GPS_PKT_ON_DELAY:
      serialClear();
      if (millis() - stateTime >= 200)
        state = GET_GPS;
      break;

    case GET_GPS:
      if (gps.available( SIMCOM )) {
        fix = gps.read(); // received an RMC!

        //DEBUG("OUT OF BOX");

        // You might want to make sure there is valid data from the GPS.
        //     It may have bad reception, so you need to decide what to do when
        //     there is no valid data.
        // You could stay in this state until the fix becomes valid,
        //    and then send the message.  Your program could get stuck here
        //    if the GPS antenna is broken.  Maybe that's what it should do.
        // Or you could go ahead and send a message without a lat/lon,
        //   and hope that the TEMP alarm will trigger multiple times.
        // With the if test commented out, it will send a message now,
        //   without a lat/lon value.
        //if (fix.valid.date && fix.valid.time && fix.valid.location) {

          // What to do with the GPS data?
          Serial.print( fix.dateTime.seconds );
          Serial.print( ' ' );
          Serial.println( fix.latitude(), 6 );

          state     = GET_GPS_DELAY;
          stateTime = millis();
        //} else {
        //   ???
        //}
      }
      break;

This assumes that you have implemented a state machine to track the different modes of the SIM800 device. And this code does not show all the commands that you may need to send. Seems like I saw a AT+CGPSRD=1 command that was also needed.

IIRC, you can also make the SIM device "forward" the GPS data to a different set of GPS_RX and GPS_TX pins. Simply connect those to your Arduino and read from those pins using a different gpsPort variable (separate from the SIM serial object). Issue #68 also mentions putting the modem in "Test Mode" to receive the GPS data.

dmnc-net commented 6 years ago

@theincredibleschnigges please try this piece of code

theincredibleschnigges commented 6 years ago

Thank you very much for the very detailed answer! I will take a look at it.