GPS is Slow and Flakey

Hello,

I'm using an MKR1500 with a GPS6MV2 board that has a NEO-6M GPS Module.

I want to make a system which can track location when the board is powered on via USB.
When the board is unplugged and the USB looses power i want the board to continue to send positional data over the MQTT Network for an hour before going to sleep until the board is powered on.

I need this system to be as reliable as possible. The Positional data doesn't need to be incredibly accurate but it must be reliable and relatively fail safe.

What i have working so far.

  • The MQTT connection works.
  • The Sleep wake functionality is stable.
  • I have seen the GPS module work.

The Problem.

  • The GPS module seems flakey at best, i've tested it in various places, and sometime it takes a crazy amount of time to find a lock.
  • other times the inbuilt Blue LED on the board starts flashing meaning it has a lock but the lat and long values aren't updated.
  • I modified the original GPS code to remove a while loop and replace it with a for loop of 50 cycles because it was causing the program lock, not sure why the original code would of had a while(1) loop which is impossible for the program to enter.

Can anyone please help ? Why does TinyGPS seem so flakey ?
Can i use the modem / networking classes to get very rough position if the GPS cant find a lock?

Here's the code.

#include "main.h"
#include <Arduino.h>
#include <TimeLib.h>
#include "ArduinoLowPower.h"
#include <MKRNB.h>
#include <ArduinoMqttClient.h>
#include <TinyGPSPlus.h>
#include <Arduino_MKRGPS.h>
#include "wiring_private.h"

// PIN Number
const char PINNUMBER[] = SECRET_PINNUMBER;

// #define serialGPS Serial1
Uart serialGPS (&sercom3, 1, 0, SERCOM_RX_PAD_1, UART_TX_PAD_0); // Create the new UART instance assigning it to pin 1 and 0

// initializing network instances.
NBClient client;
MqttClient mqttClient(client);
NBModem modem;
GPRS gprs;
NB nbAccess;

// initializing GPS instances
TinyGPSPlus gps;

// sleep & wake variables.
const int powerDetectionPin = 5;
bool powerd = true;
bool hasBeenWoken;
bool wasSentToSleep;

// GPS variables. 
float latitude;
float longitude;
int sattaliteCount;

// Sending data length variables
unsigned long unpluggedTime;
unsigned long dataSendingDuration = 60 * 60 * 1000; // n Minutes

// Publishing Frequency Variables
unsigned long previousMillis = 0;
unsigned long interval = 1 * 60 * 1000; // n Minutes

void charging() {
  hasBeenWoken = true;
}

void connectToNetwork()
{
  Serial.println("Setting up network...");

  // connection state
  boolean connected = false;

  Serial.print("Modem Initialization ...");

  // After starting the modem with NB.begin()
  // attach to the GPRS network with the APN, login and password
  while (!connected)
  {
    Serial.print(".");

    if ((nbAccess.begin(PINNUMBER) == NB_READY) &&
        (gprs.attachGPRS() == GPRS_READY))
    {
      connected = true;
      Serial.println("Connected");
    }
    else
    {
      Serial.println("Not connected. ");
      delay(1000);
    }
  }

  // prints the modems IMEI number cut and paste into Adafruit cloud
  Serial.println(modem.getIMEI());
  mqttClient.setId(modem.getIMEI());
  mqttClient.setUsernamePassword(AIO_USERNAME, AIO_KEY);

  Serial.print("MQTT Initialization ...");
  if (!mqttClient.connect(AIO_SERVER, AIO_SERVERPORT))
  {
    Serial.print("MQTT connection failed! Error code = ");
    Serial.println(mqttClient.connectError());

    // Rerunning Setup if connection fails.
    setup();
  } else {
    Serial.println("Connected");
  }
}

void connectGPS(){
  Serial.print("GPS Initialization.");
  Serial.println(serialGPS.read());

  // - - - - - - GPS Section - - - - - -
  for (int i = 0; i < 50; i++) {
    if(serialGPS.available()) {
      Serial.print(".");
      if(gps.encode(serialGPS.read()))
      {

        gps.location.lat();
        // sattaliteCount = gps.satellites.value();
        if(gps.location.isValid())
        {
          Serial.print("decoding sucsesful.");
          latitude = gps.location.lat();
          longitude = gps.location.lng();
        }
        Serial.print("enconding valid.");
        break;
      }
      delay(50);
    }
  }
  Serial.println("");
}


void setup() {
  for (int i = 0; i < 20; i++) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
    digitalWrite(LED_BUILTIN, LOW);
    delay(100);
  }
  delay(1000);

  serialGPS.begin(9600);
  // GPS Input and Output Pins.
  pinPeripheral(1, PIO_SERCOM); //Assign RX function to pin 1
  pinPeripheral(0, PIO_SERCOM); //Assign TX function to pin 0

  // put your setup code here, to run once:
  pinMode(powerDetectionPin, INPUT_PULLUP);

  // Wake up interupt when board is plugged in. 
  LowPower.attachInterruptWakeup(powerDetectionPin, charging, RISING);
  connectToNetwork();
}


void loop() {
  unsigned long currentMillis = millis();

  // initiating Setup after wake up.  
  if(hasBeenWoken && wasSentToSleep){
    hasBeenWoken = false;
    wasSentToSleep = false;
    setup();
  }

  // Updating GPS Position. 
  connectGPS();

  // - - - - - - SENDING DATA SECTION - - - - - - 
  if(currentMillis - previousMillis >= interval)
  {
    // save the last time a message was sent
    previousMillis = currentMillis;
    Serial.println("Publishing Data");

    // Check to to stop 0, 0 being sent before GPS lock. 
    if(latitude != 0.0 && longitude != 0.0){
      Serial.println("Lat: " + String(latitude) + " - Long: " + String(longitude) );
      // Updating the dashboard with location. 
      mqttClient.beginMessage(AIO_FEED_LOCATION);
      mqttClient.print("{\"lat\":");
      mqttClient.print(latitude);
      mqttClient.print(",");
      mqttClient.print("\"lon\":");
      mqttClient.print(longitude);
      mqttClient.println("}");
      mqttClient.endMessage();
    }

    // Updating the dashboard Logs
    mqttClient.beginMessage(AIO_FEED_LOGS);
    mqttClient.println(" - Modem IMEI number: " + String(modem.getIMEI()));
    mqttClient.println(" - currentMillis: " + String(currentMillis));
    mqttClient.println(" - unpluggedTime: " + String(unpluggedTime));
    mqttClient.println(" - difference: " + String(currentMillis - unpluggedTime));
    mqttClient.println(" - threshhold: " + String(dataSendingDuration));
    mqttClient.println(" - sattaliteCount: " + String(sattaliteCount));
    mqttClient.println(" - GPS Coordinates: " + String(latitude) + " latitude, " + String(longitude) + " longitude");
    mqttClient.println(" - digitalRead(5): " + String(digitalRead(5)));
    mqttClient.endMessage();

    for (int i = 0; i < 2; i++) {
      digitalWrite(LED_BUILTIN, HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN, LOW);
      delay(100);
    }
    delay(1000);
    for (int i = 0; i < 2; i++) {
      digitalWrite(LED_BUILTIN, HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN, LOW);
      delay(100);
    }
  }


  // - - - - - - SLEEP CONTROL SECTION - - - - - -
  // Check when board has been unplugged for first time. 
  if(!digitalRead(5) && powerd){
    powerd = false;
    // recording time when unplugged. 
    unpluggedTime = currentMillis;
    for (int i = 0; i < 7; i++) {
      digitalWrite(LED_BUILTIN, HIGH);
      delay(200);
      digitalWrite(LED_BUILTIN, LOW);
      delay(200);
    }
    delay(1000);
  } 
  // resetting after being re plugged.
  else if (digitalRead(5) && !powerd) {
    powerd = true;
  }

  // updating times whilst board is plugged in. 
  if(digitalRead(5)){
    unpluggedTime = currentMillis;
  }

  // Final Check before sending board to sleep. 
  if((currentMillis - unpluggedTime > dataSendingDuration)){
    for (int i = 0; i < 10; i++) {
      digitalWrite(LED_BUILTIN, HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN, LOW);
      delay(100);
    }
    delay(1000);

    wasSentToSleep = true;
    LowPower.sleep();

    for (int i = 0; i < 10; i++) {
      digitalWrite(LED_BUILTIN, HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN, LOW);
      delay(100);
    }
  }
  // Makes LED flash to show loop is turning. 
  for (int i = 0; i < 5; i++) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(500);
    digitalWrite(LED_BUILTIN, LOW);
    delay(500);
  }
  Serial.print(".");
  delay(2000);
}

void SERCOM3_Handler()
{
  serialGPS.IrqHandler();
}

Links, please? You posted in the wrong sub category. This is not for projects.

For one, you are not reading the GPS correctly. The code should be reading it more or less continuously, otherwise most GPS sentences are dropped. You must be outside, with a clear view of the sky, for reliable reception.

  // - - - - - - GPS Section - - - - - -
  for (int i = 0; i < 50; i++) {
    if(serialGPS.available()) {
      Serial.print(".");
      if(gps.encode(serialGPS.read()))

Study the TinyGPS library examples to learn how this is done properly,

Rough positioning is termed "assisted GPS" or A-GPS.
You can read about the IoT implementation here:
What Is Assisted GPS? (iotforall.com)

I have not seen an Arduino implementation, but have not looked beyond a quick 'n dirty Google search.

I have read through the TinyGPS examples, I originally used one like this.

  while (*gpsStream)
    if (gps.encode(*gpsStream++))
      displayInfo();

... however most of the examples use a while loop like this which completely lock the board inside the while loop if something goes wrong.

I can't find an example which offers a fail safe GPS system, which if something goes wrong it doesn't lock the rest of the program.

Anyone have a suggestion ?

Only if you fail to check for errors and exit the loop. Exercise your programming skills.

If you want the GPS to be reliable, reading it should be the highest priority task in the system.

It takes maybe a microsecond for the Arduino to check whether there are characters in the buffer, and if not, do something else.

The NEO M6N that I have were very very cheap, but behave as you describe. It takes a loooong time to get a lock and they only do GPS. No Galileo, Glonass etc.

A M8N would already be better, however when you buy them from Ali or Ebay, you wiill get a clone almost for sure. At least below is what I got and there is no memory chip on the board. after I pulled of the metal cap.

The memory chip on both boards is the 8 pin SOIC on the main board.

Can you provide an example ? ....

I Know, but that's not what I had hoped to see under the "tincan"
Impossible to upgrade the firmware on my module. and I have no clue if the I2C SOIC memory chip is used at all as the module does not remember any of it's settings between a cold restart.

I had hoped to see this..

I think you are focusing unduly on the module. To be sure that it is the cause of the problem, you can't mediate with TinyGPS. You have to evaluate the NMEA sentences directly by examination. Else, run your code with a module that is known good.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.