Creating a GPS Tracker For Remote Locations and Transmitting Via LoRaWAN

Greetings, I am working on a project and have broken it down into byte sized chunks (pun intended). The idea is to make a GPS tracker that communicates over LoRa which will be set up at a base camp. This project is used to ensure campers have a safe environment and the counselors can see if anyone needs aid.

Stuff Used:

Arduino UNO
Xbee Shield
Multitech mDot
Adafruit Ultimate GPS V3

Part 1

I started and solved how to connect the end device to the LoRa server and how to send strings via LoRaWAN protocol. The following is the working code:

#include <SoftwareSerial.h>

SoftwareSerial mySerial(2, 3); // RX, TX

void setup() {

  // AT Commands for MultiTech

  Serial.println("Joining Lora Network");
  
  mySerial.begin(115200);
  //Test
  mySerial.println("AT");
  //OTAA network ID
  mySerial.println("AT+NI=1,Ky-Newton");
  //OTAA network key
  mySerial.println("AT+NK=1,Ky-Newton");
  //frequency sub band 3
  mySerial.println("AT+FSB=3");
  //join delay 5 sec
  mySerial.println("AT+JD=5");

  mySerial.println("AT+NJM=1");

  mySerial.println("AT+TXDR=1");
  //store in memory
  mySerial.println("AT&W");
  //reset
  mySerial.println("ATZ");
  delay(5000);
  //Join network
  mySerial.println("AT+JOIN");
  delay(1000);

}

void loop() {


  //Join network
  //mySerial.println("AT+JOIN");
  delay(1000);
  //send message
  mySerial.println("AT+SEND=Hello World");
  // Reading the output in terminal

  if (mySerial.available()) {

    Serial.write(mySerial.read());

  }

}

Part 2

Here I solved how to use the GPS and then string the latitude and longitude and ensured I could send the string variable, again working code is below:

#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>

// Connect the GPS Power pin to 5V
// Connect the GPS Ground pin to ground
// Connect the GPS TX (transmit) pin to Digital 5
// Connect the GPS RX (receive) pin to Digital 4

SoftwareSerial mySerial(5, 4);
Adafruit_GPS GPS(&mySerial);

// Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console
// Set to 'true' if you want to debug and listen to the raw GPS sentences
#define GPSECHO  false

void setup()
{

  // connect at 115200 so we can read the GPS fast enough and echo without dropping chars
  // also spit it out
  Serial.begin(115200);

  // setup delay
  delay(5000);

  // 9600 NMEA is the default baud rate for Adafruit MTK GPS's- some use 4800
  GPS.begin(9600);

  delay(1000);
  // Ask for firmware version
  mySerial.println(PMTK_Q_RELEASE);
}

uint32_t timer = millis();
void loop()                     // run over and over again
{
  int siz = 0;
  char myLat[9]; //hold lattitude
  char myLon[9]; //hold lognitude
  char myString[100]; //Empty string with the limit of 53 bytes
  char c = GPS.read();

  // if you want to debug, this is a good time to do it!
  if ((c) && (GPSECHO))
    Serial.write(c);

  // if a sentence is received, we can check the checksum, parse it...
  if (GPS.newNMEAreceived()) {
    // a tricky thing here is if we print the NMEA sentence, or data
    // we end up not listening and catching other sentences!
    // so be very wary if using OUTPUT_ALLDATA and trytng to print out data
    //Serial.println(GPS.lastNMEA());   // this also sets the newNMEAreceived() flag to false

    if (!GPS.parse(GPS.lastNMEA()))   // this also sets the newNMEAreceived() flag to false
      return;  // we can fail to parse a sentence in which case we should just wait for another
  }

  // approximately every 2 seconds or so, print out the current stats
  if (millis() - timer > 2000) {
    timer = millis(); // reset the timer

    if (GPS.fix) {

      dtostrf(GPS.latitude, 7, 2, myLat);
      dtostrf(GPS.longitude, 7, 2, myLon);

      strcat(myString, "AT+SEND=");
      strcat(myString, "D01,");
      strcat(myString, myLat);
      strcat(myString, ",");
      strcat(myString, myLon);
      //concatanate the rest here as above.
      Serial.println(myString);
      strcpy(myString, "");
    }
  }
}

Now for part 3 I am trying to combine the codes so that I can send the string to the gateway and I do still get affirmed connection to the gateway which is handled first in the setup but I am seeing zero attempts to actually send the string. It should also be mentioned that the GPS is attaining a fix which means its setup has gone through as well. here is the combined code:

#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>

// Connect the GPS Power pin to 5V
// Connect the GPS Ground pin to ground
// Connect the GPS TX (transmit) pin to Digital 5
// Connect the GPS RX (receive) pin to Digital 4

SoftwareSerial mySerial2(2, 3); // RX, TX
SoftwareSerial mySerial1(5, 4);

Adafruit_GPS GPS(&mySerial1);

// Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console
// Set to 'true' if you want to debug and listen to the raw GPS sentences
#define GPSECHO  false

void setup()
{
  //-------------------------------------LoRaWAN-Setup-----------------------------------------------------------
  // AT Commands for MultiTech

  Serial.println("Joining Lora Network");
  
  mySerial2.begin(115200);
  //Test
  mySerial2.println("AT");
  //OTAA network ID
  mySerial2.println("AT+NI=1,Ky-Newton");
  //OTAA network key
  mySerial2.println("AT+NK=1,Ky-Newton");
  //frequency sub band 3
  mySerial2.println("AT+FSB=3");
  //join delay 5 sec
  mySerial2.println("AT+JD=5");

  mySerial2.println("AT+NJM=1");

  mySerial2.println("AT+TXDR=1");
  //store in memory
  mySerial2.println("AT&W");
  //reset
  mySerial2.println("ATZ");
  delay(5000);
  //Join network
  mySerial2.println("AT+JOIN");
  delay(1000);
  mySerial2.end();
  //--------------------------------------GPS-Setup--------------------------------------------------------------
  // connect at 115200 so we can read the GPS fast enough and echo without dropping chars
  // also spit it out
  mySerial1.begin(115200);

  // setup delay
  delay(5000);

  // 9600 NMEA is the default baud rate for Adafruit MTK GPS's- some use 4800
  GPS.begin(9600);

  delay(1000);
  // Ask for firmware version
  mySerial1.println(PMTK_Q_RELEASE);
  // End Communication
  mySerial1.end();
}

uint32_t timer = millis();
void loop()                     // run over and over again
{
//-------------------------------------------GPS----------------------------------------------------------
  mySerial1.begin(115200);

  //delay
  delay(5000);

  int siz = 0;
  char myLat[9]; //hold lattitude
  char myLon[9]; //hold lognitude
  char myString[100]; //Empty string
  char c = GPS.read();

  // if you want to debug, this is a good time to do it!
  if ((c) && (GPSECHO))
    mySerial1.write(c);

  // if a sentence is received, we can check the checksum, parse it...
  if (GPS.newNMEAreceived()) {
    // a tricky thing here is if we print the NMEA sentence, or data
    // we end up not listening and catching other sentences!
    // so be very wary if using OUTPUT_ALLDATA and trytng to print out data
    //Serial.println(GPS.lastNMEA());   // this also sets the newNMEAreceived() flag to false

    if (!GPS.parse(GPS.lastNMEA()))   // this also sets the newNMEAreceived() flag to false
      return;  // we can fail to parse a sentence in which case we should just wait for another
  }

  // approximately every 2 seconds or so, print out the current stats
  if (millis() - timer > 5000) {
    timer = millis(); // reset the timer

    if (GPS.fix) {

      dtostrf(GPS.latitude, 7, 2, myLat);
      dtostrf(GPS.longitude, 7, 2, myLon);

      strcat(myString, "AT+SEND=");
      strcat(myString, "D01,");
      strcat(myString, myLat);
      strcat(myString, ",");
      strcat(myString, myLon);
      Serial.println(myString);
      mySerial1.end();
      //concatanate the rest here as above.
 //--------------------------------------------------LoRaWAN---------------------------------------------------------
      mySerial2.begin(115200);

      //delay
      delay(5000);
      //send message
      mySerial2.println(myString);
      //clear string
      strcpy(myString, "");
      // Reading the output in terminal
      if (mySerial2.available()) {
        Serial.write(mySerial2.read());
      }
      mySerial2.end();
    }
  }
}

Has anyone attempted something like this before and if so do you see where I may have went wrong trying to combine the two parts? Any help is appreciated.

Sincerely,
Josh

The Software serial reference may have the answer;

"The library has the following known limitations:
If using multiple software serial ports, only one can receive data at a time."

There were three major issues:

  1. I was calling for a serial print before i did a serial begin.
  2. I had allocated too much memory to a dummy string.
  3. I was using serial.end() and should have instead used serial.listen().

Thanks for the reply and here is the fixed version if anyone needs.

// Code for GPS communication and mDot transfer of data to gateway via LoRaWAN OTAA
//Author: Joshua L. Hayes
//jhayesee2020@gmail.com
//Function: Takes in information from GPS and Bluetooth then creates a string that is sent over LoRaWAN protical
//          string structer is deviceID,Lattitude,Longitude,HeartRate,Temperature,step count, the commas will act like a delimiter
//          for the mapping software to know the end of one data and the begining of the next.
//Notes: Bluetooth coding to be added as soon as possible to enable biometric data transfer authored by Edward Ojini and spliced in by Josh Hayes.

#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>

int siz = 0;
char myLat[10];
char myLon[10];
char myString[50]; //Empty string

// Connect the GPS Power pin to 5V
// Connect the GPS Ground pin to ground
// Connect the GPS TX (transmit) pin to Digital 5
// Connect the GPS RX (receive) pin to Digital 4

SoftwareSerial mySerial2(2, 3); // RX, TX
SoftwareSerial mySerial1(5, 4);

Adafruit_GPS GPS(&mySerial1);

// Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console
// Set to 'true' if you want to debug and listen to the raw GPS sentences
#define GPSECHO  false

void setup()
{
  //-------------------------------------LoRaWAN------------------------------------------------------------
  // AT Commands for MultiTech
  Serial.begin(9600);
  delay(100);
  Serial.println("Joining Lora Network");
  mySerial2.begin(115200);
  delay(200);
  //Test
  mySerial2.println("AT");
  delay(100);
  //OTAA network ID
  mySerial2.println("AT+NI=1,Ky-Newton"); // Match your network name
  delay(100);
  //OTAA network key
  mySerial2.println("AT+NK=1,Ky-Newton"); // Match your password
  delay(100);
  //frequency sub band 3
  mySerial2.println("AT+FSB=3");
  delay(100);
  //join delay 5 sec
  mySerial2.println("AT+JD=5");
  delay(100);

  mySerial2.println("AT+NJM=1");
  delay(100);

  mySerial2.println("AT+TXDR=1");
  delay(100);
  //store in memory
  mySerial2.println("AT&W");
  delay(100);
  //reset
  mySerial2.println("ATZ");
  delay(5000);
  //Join network
  mySerial2.println("AT+JOIN");
  delay(1000);
  //-------------------------------------End LoRaWAN------------------------------------------------------------

  // setup delay
  delay(5000);

  //-------------------------------------GPS--------------------------------------------------------------------
  // 9600 NMEA is the default baud rate for Adafruit MTK GPS
  GPS.begin(9600);
  delay(1000);
  // SET OUTPUT FORMAT
  mySerial1.println(PMTK_Q_RELEASE);
  //-------------------------------------eND GPS--------------------------------------------------------------------
  mySerial1.listen();
}

uint32_t timer = millis();
void loop()
{
  // READ GPS SERIAL AND CHECK FOR NEW GPS LOCATION
  char c = GPS.read();
  if (GPS.newNMEAreceived()) {

    // TRY TO PARSE LAST GPS SENTENCE
    if (!GPS.parse(GPS.lastNMEA())) {
      Serial.println("Warning: failed to parse GPS NMEA message");
      return;
    }
  }

  // SEND GPS FIX EVERY 5 SECONDS
  if (millis() - timer > 5000) {
    // RESET THE TIMER
    timer = millis();

    // IF WE HAVE A 3D FIX, FILL OUT OUTPUT STRING WITH COORDINATES
    if (GPS.fix) {

      // FORMAT MESSAGE
      Serial.println(GPS.latitude, 4);
      Serial.println(GPS.longitude, 4);

      dtostrf(GPS.latitude, 9, 3, myLat);
      dtostrf(GPS.longitude, 9, 3, myLon);

      strcat(myString, "AT+SEND=");
      strcat(myString, "D01,");
      strcat(myString, myLat);
      strcat(myString, ",");
      strcat(myString, myLon);



      // DEBUG THE MESSAGE
      Serial.println(myString);

      // SEND MESSAGE TO LORAWAN MODEM
      mySerial2.println(myString);
      memset(myString, 0, strlen(myString));
    }
  }

Using LoRaWAN requires the use of a nearby, 2 or 3km away perhaps, LoRaWAN gateway.

So its not really a suitable system for a GPS tracker to be used in 'remote locations'.

In this use case it is. It is a 190 acre plot in a remote location that hosts a non profit camp where kids learn about engineering and science. I only need the system to reach a half mile and right now with my spread I'm at .67 mile and getting 53 bytes of data transference every 5sec so it is great for our needs. Thanks for your comments.

Looks good!

To be safer, it is strongly recommended to use the "n" versions of the string functions, e.g. strncpy(), strncat(), etc. as they will never write past the end of your output buffer, trashing memory.

Even if the program seems to be working correctly now, this small change can't hurt and it may save you from future crashes.

Thanks for the heads up! changes made accordingly.