AT+CGNSINF parsing and validation

  1. I need a way on how to validate whether the gps module has received valid coordinates or not but to no avail I can’t think of a way to do so is there anyway to look at the number of satellites that have homed in on the module? and if I do fing a way to validate the output what is the best course of action I just though to just call the getcoord() function again but that seems at little bit inefficient

  2. second question why is it when I load the serial it gives me incomplete results but when I reload it again it gives me correct results

  3. this maybe a hardware related question but does the gsm module blinking blue LED mean that it’s picking up gps signals?..

here is my code for what I’m trying to do

void setup()
{
Serial.begin(19200);
mySerial.begin(19200);  //Default serial port setting for the GPRS modem is 19200bps 8-N-1
mySerial.print("\r");
delay(1000);                    //Wait for a second while the modem sends an "OK"

Serial.print( getcoord() );
}

String getcoord()
{
  char coord[256];
  int i=0;
  String msgtype = "RMC";

  mySerial.print( "AT+CGNSPWR=1\r");
  delay( 1000 );
  mySerial.print( "AT+CGNSSEQ=\"" + msgtype + "\"" + "\r" );
  delay( 1000 );
  mySerial.print( "AT+CGNSINF" );

  while( mySerial.available()!=0 && i<( sizeof(coord) - 1 ) )
  {
   coord[i] = (char)mySerial.read();
   i++; 
  }
  coord[i] = '\0';
  
  String result = String(coord);
  String longti = result.substring(36 , 45);
  String lati = result.substring(45 , 56);
  String coordinates = longti + lati;
    
  Serial.println("Result = " + result);
  Serial.println("Longtitude = " + longti);
  Serial.println("Latitude = " + lati);
  Serial.println("Coordinates = " + coordinates);

  return coordinates;
}

there are libraries that do what you want.

here is a simple parsing tool that I did a while back for another forum member, has to be modified for your string. You can test it with putting the Example Message into the Serial Monitor:

#include "SerialMessenger.h"

/*
 * Example Message
 * $GPRMC,001225,A,2832.1834,N,08101.0536,W,12,25,251211,1.2,E,A*03
 */

void parseSerialMssg(const char* mssg);
void parseSoftSoftSerialMssg(const char* mssg);

SoftwareSerial softSerial(4,5);

SerialMessenger serial(Serial, 254, '

uses SerialMessenger.h

#ifndef SERIALMESSENGER_H
#define SERIALMESSENGER_H

#include <SoftwareSerial.h>

#if ARDUINO >= 100
  #include "Arduino.h"
  #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
    #define MAX_SERIAL_PORTS 2
  #elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__)
    #define MAX_SERIAL_PORTS 1
  #elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    #define MAX_SERIAL_PORTS 5
  #endif
#else
  #define MAX_SERIAL_PORTS 2
#endif

typedef uint8_t byte_size_t; 



class SerialMessenger {
  public:
    SerialMessenger(HardwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, void (*parseFunction)(const char*), bool err = false);
    SerialMessenger(SoftwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, void (*parseFunction)(const char*), bool err = false);
    ~SerialMessenger();
    void begin(uint32_t baudRate);
    char* checkForValidMessage(const char startMarker, const char endMarker);
    static void update(void);
    
  private:
    HardwareSerial* hwStream;
    SoftwareSerial* swStream;
    Stream* stream;
    void (*callback)(const char*);
    char* incomingMessage;
    byte_size_t bufferSize;
    byte_size_t idx = 0;
    char startMkr;
    char endMkr;
    bool errors;
    static byte_size_t instanceCount;
    static SerialMessenger* instances[MAX_SERIAL_PORTS];
    enum {
      WAITING,
      PARSING,
    }state = WAITING;
};

#endif

and SerialMessenger.cpp

#include "SerialMessenger.h"
#include "SoftwareSerial.h"

byte_size_t SerialMessenger::instanceCount = 0;
SerialMessenger* SerialMessenger::instances[MAX_SERIAL_PORTS] = {nullptr};

SerialMessenger::SerialMessenger(HardwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, void (*parseFunction)(const char*), bool err)
{
  startMkr = startMarker;
  endMkr = endMarker;
  instances[instanceCount++] = this;
  hwStream = &device;
  callback = parseFunction;
  bufferSize = maxMessageLength > 254? 254 : maxMessageLength;
  incomingMessage = new char[bufferSize + 1];
  errors = err;
}

SerialMessenger::SerialMessenger(SoftwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, void (*parseFunction)(const char*), bool err)
{
  startMkr = startMarker;
  endMkr = endMarker;
  instances[instanceCount++] = this;
  swStream = &device;
  callback = parseFunction;
  bufferSize = maxMessageLength > 254? 254 : maxMessageLength;
  incomingMessage = new char[bufferSize + 1];
  errors = err;
}

SerialMessenger::~SerialMessenger(){ free(incomingMessage); incomingMessage = NULL;}

void SerialMessenger::update()
{
  for (size_t i = 0; i < instanceCount; i++)
  {
    if (const char* message = instances[i]->checkForValidMessage(instances[i]->startMkr, instances[i]->endMkr))
    {
      instances[i]->callback(message);
    }
  }
}

char* SerialMessenger::checkForValidMessage(const char startMarker, const char endMarker) //, const char checksumToken)
{
  stream = !hwStream ? (Stream*)swStream : hwStream;
  if (stream->available())
  {
    if (state == WAITING)
    {
      if (startMkr)
      {
        if (stream->read() == startMarker)
        {
          state = PARSING;
        }
        return nullptr;
      }
      else
      {
        state = PARSING;
      }
    }
    else if (state == PARSING)
    {
      incomingMessage[idx] = stream->read();
      if (incomingMessage[idx] == endMarker)
      {
        incomingMessage[idx] = '\0';
        idx = 0;
        if (startMkr)
        {
          state = WAITING;
        }
        return incomingMessage;
      }
      else
      {
        idx++;
        if (idx > bufferSize - 1)
        {
          if (errors)
          {
            stream->println(F("Bad Message"));  //<< Error back to sender
          }
          idx = 0;
          incomingMessage[idx] = '\0';
          if (startMkr)
          {
            state = WAITING;
          }
        }
      }
    }
  }
  return nullptr;
}

void SerialMessenger::begin(uint32_t baudRate)
{
  if (hwStream)
  {
    hwStream->begin(baudRate);
  }
  else
  {
    swStream->begin(baudRate);
  }
}

, ‘\n’, parseSerialMssg);  // defining start & end markers as ’


uses SerialMessenger.h

§DISCOURSE_HOISTED_CODE_1§


and SerialMessenger.cpp

§DISCOURSE_HOISTED_CODE_2§


 and '\n' respectively

SerialMessenger sw(softSerial, 254, '

uses SerialMessenger.h

§_DISCOURSE_HOISTED_CODE_1_§

and SerialMessenger.cpp

§_DISCOURSE_HOISTED_CODE_2_§

, ‘\n’, parseSerialMssg);
//SerialMessenger serial1(Serial1, 16, ‘\0’, ‘\n’, parseMssg);

void setup()
{
  serial.begin(9600);
  sw.begin(9600);
}

void loop()
{
  SerialMessenger::update();
}

void parseSerialMssg(const char* mssg)
{
  Serial.print(F(“From callback function:\t”));
  Serial.println(mssg);
  if (strstr(mssg, “GPRMC”))
  {
    Serial.print(F(“Parsing GPRMC sentence…\n”));
    char gprmc[strlen(mssg) + 1];
    char fieldData[16];
    strcpy(gprmc, mssg);
    strtok(gprmc, “,”);
    Serial.println(gprmc);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Time:\t”));
    Serial.println(fieldData);
    int hour = (fieldData[0] - ‘0’) * 10 + (fieldData[1] - ‘0’);
    int minute = (fieldData[2] - ‘0’) * 10 + (fieldData[3] - ‘0’);
    int second = (fieldData[4] - ‘0’) * 10 + (fieldData[5] - ‘0’);
    snprintf(fieldData, sizeof(fieldData), “Time:\t%d:%02d:%02d”, hour, minute, second);
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Status:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Lat:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Dir:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Lon:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Dir:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Speed:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Angle:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Date:\t”));
    Serial.println(fieldData);
    int year = (fieldData[0] - ‘0’) * 10 + (fieldData[1] - ‘0’);
    int month = (fieldData[2] - ‘0’) * 10 + (fieldData[3] - ‘0’);
    int day = (fieldData[4] - ‘0’) * 10 + (fieldData[5] - ‘0’);
    snprintf(fieldData, sizeof(fieldData), “Date:\t%2d/%02d/%02d”, month, day, year);
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Var:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“Dir:\t”));
    Serial.println(fieldData);
    strcpy(fieldData, strtok(NULL, “,”));
    Serial.print(F(“ChkSum:\t”));
    Serial.println(fieldData);
  }
  else if (strstr(mssg, “GPGGA”))
  {
    Serial.print(F(“Parsing GPGGA sentence…\n”));
  }
}

//void parseSoftSoftSerialMssg(const char* mssg)
//{
//  Serial.print(F(“New SoftSerial Message:\t”));
//  Serial.println(mssg);
//}


uses SerialMessenger.h

§DISCOURSE_HOISTED_CODE_1§


and SerialMessenger.cpp

§DISCOURSE_HOISTED_CODE_2§

BulldogLowell:
here is a simple parsing tool

Uh…

  • it requires SoftwareSerial and HardwareSerial, instead of using Stream.

  • it uses malloc and free, aka “dynamic memory”. Not recommended here.

  • it uses the ternary operator.

  • it uses callback functions.

I’m not sure that qualifies as a “simple parsing tool”. o_O

@k5nn, I would suggest starting with Serial Input Basics to receive the complete response line from the SIM800 device. Then parse the line using strtok, atof and atoi:

const byte numChars = 100; // spec says up to 94 characters
char receivedChars[ numChars ];

   ...  Serial Input Basics sketch ...

void showNewData()
{
  if (newData == true) {
    // After final CR/LF received in char array "response":
    
    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();

    newData = false;
  }
}

Notice that this avoids using the String class, a potential source of many frustrating problems, espeically when the sketch has been running a long time.

I would also recommend something besides SoftwareSerial. Here is a good summary of the choices.

-dev:

  • it requires SoftwareSerial and HardwareSerial, instead of using Stream.

Parsing Serial Data requires using HardwareSerial or SoftwareSerial (or some alternative to SoftwareSerial) so how could that matter…

-dev:

  • it uses malloc and free, aka “dynamic memory”. Not recommended here.

Here we are using global instances of the library, so that point is meaningless…

-dev:

  • it uses the ternary operator.

Seriously? besides, that’s all “behind the curtain” stuff in the library…

-dev:

  • it uses callback functions.

yes, to encapsulate the handler and make it simple to use.