Skip to content

Instantly share code, notes, and snippets.

@nobodyguy
Last active September 21, 2022 03:13
Show Gist options
  • Save nobodyguy/7920482dd00f300b973126f9d7188db4 to your computer and use it in GitHub Desktop.
Save nobodyguy/7920482dd00f300b973126f9d7188db4 to your computer and use it in GitHub Desktop.
GpsLoRaWANTracker using Arduino, RN2483 and L86/neo-m8n GPS module
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <avr/sleep.h>
#include <avr/power.h>
// ----------------------------------------------
// DEFINES
// ----------------------------------------------
#define rxGpsPin 10
#define txGpsPin 11
#define ledPin 6
#define loraResetPin 4
#define loraTxPin 8
#define loraRxPin 7
#define wakePin 2
#define sleepTime 60000 // 1 minute
#define minSatelites 8
const String devaddr = "XXXXXXXX";
const String appskey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
const String nwkskey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
// ----------------------------------------------
// DEBUG TOOLS
// ----------------------------------------------
#define DEBUG
#ifdef DEBUG
void trace(String str) {
Serial.println(str);
}
#else
#define trace(x)
#endif
// ----------------------------------------------
// GLOBALS
// ----------------------------------------------
String str;
SoftwareSerial gpsSerial = SoftwareSerial(rxGpsPin, txGpsPin);
SoftwareSerial loraSerial = SoftwareSerial(loraRxPin, loraTxPin);
TinyGPSPlus gps;
// ----------------------------------------------
// SLEEP FUNCTIONS
// ----------------------------------------------
void wakeUpArduino(void)
{
/* This will bring us back from sleep. */
/* We detach the interrupt to stop it from
* continuously firing while the interrupt pin
* is low.
*/
detachInterrupt(0);
}
void sleepArduino(void)
{
/* Setup pin2 as an interrupt and attach handler. */
attachInterrupt(0, wakeUpArduino, CHANGE);
delay(100);
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_mode();
/* The program will continue from here. */
/* First thing to do is disable sleep. */
sleep_disable();
}
// ----------------------------------------------
// SETUP
// ----------------------------------------------
void setup() {
// put your setup code here, to run once:
#ifdef DEBUG
Serial.begin(9600);
while (!Serial);
#endif
loraSerial.begin(57600);
while (!loraSerial);
//pinMode(rxGpsPin, INPUT);
//pinMode(txGpsPin, OUTPUT);
gpsSerial.begin(9600);
pinMode(wakePin, INPUT);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, HIGH);
// reset the module
pinMode(loraResetPin, OUTPUT);
digitalWrite(loraResetPin, LOW);
delay(500);
digitalWrite(loraResetPin, HIGH);
delay(2000);
loraSerial.setTimeout(50000);
loraSerial.listen();
trace("reset");
//str = loraSerial.readStringUntil('\n');
//trace(str);
trace("init");
loraSerial.println("mac set devaddr " + devaddr);
str = loraSerial.readStringUntil('\n');
trace(str);
loraSerial.println("mac set appskey " + appskey);
str = loraSerial.readStringUntil('\n');
trace(str);
loraSerial.println("mac set nwkskey " + nwkskey);
str = loraSerial.readStringUntil('\n');
trace(str);
//loraSerial.println("mac set dr 5");
//str = loraSerial.readStringUntil('\n');
//trace(str);
loraSerial.println("mac save");
delay(2000);
str = loraSerial.readStringUntil('\n');
trace(str);
loraSerial.println("mac join abp");
delay(2000);
str = loraSerial.readStringUntil('\n');
trace(str);
str = loraSerial.readStringUntil('\n');
trace(str);
digitalWrite(ledPin, LOW);
gpsSerial.listen();
trace("GPS listening...");
}
double lat;
double lng;
char latBuffer[20];
char lngBuffer[20];
char payload[100];
// ----------------------------------------------
// MAIN LOOP
// ----------------------------------------------
void loop() {
while (gpsSerial.available())
{
if (gps.encode(gpsSerial.read()))
{
int satellites = gps.satellites.value();
//Serial.print(satellites);
if (gps.satellites.isValid() && (satellites >= minSatelites) && ((gps.location.isValid() && gps.location.age() < 2000)) && (gps.location.lat() > 0) && (gps.location.lng() > 0)) {
digitalWrite(ledPin, HIGH);
lat = gps.location.lat();
lng = gps.location.lng();
dtostrf(lat, 10, 8, latBuffer);
dtostrf(lng, 10, 8, lngBuffer);
sprintf(payload, "%s,%s,%d", latBuffer, lngBuffer, satellites);
gpsSerial.end();
loraSerial.listen();
sendRadioMsgBuffer(payload);
str = loraSerial.readStringUntil('\n');
trace(str);
if (str.startsWith("ok"))
{
str = loraSerial.readStringUntil('\n');
trace(str);
if (str.startsWith("not_joi")) {
loraSerial.println("mac join abp");
delay(3000);
str = loraSerial.readStringUntil('\n');
str = loraSerial.readStringUntil('\n');
trace(str);
if (str.startsWith("acc")) {
sendRadioMsgBuffer(payload);
str = loraSerial.readStringUntil('\n');
trace(str);
}
} else if (!str.startsWith("mac_tx_ok") && !str.startsWith("mac_rx")) {
errorLoop();
}
}
else
{
errorLoop();
}
digitalWrite(ledPin, LOW);
loraSerial.print("sys sleep ");
loraSerial.println(sleepTime);
delay(100);
trace("Sleeping...");
sleepArduino(); // put arduino into sleep - it will be woken up by lora module
gpsSerial.begin(9600);
gpsSerial.listen();
}
}
}
}
// ----------------------------------------------
// RADIO FUNCTIONS
// ----------------------------------------------
void sendRadioMsgBuffer(char* msg)
{
size_t len = strlen(msg);
loraSerial.print("mac tx cnf 1 ");
for (int i = 0; i < len; i++) {
loraSerial.print(msg[i], HEX);
}
loraSerial.println();
Serial.println("Message was sent");
}
void sendRadioMsg(String msg)
{
char charBuf[200];
msg.toCharArray(charBuf, 200);
sendRadioMsgBuffer(charBuf);
}
void errorLoop()
{
while (true)
{
digitalWrite(ledPin, HIGH);
delay(1000);
digitalWrite(ledPin, LOW);
delay(1000);
}
}
Copy link

ghost commented Mar 15, 2017

Thank you very much for sharing. As id saw, id always missunderstood sprintf.
Im using SPI to communicate with my LoRa module.


//pin 6 LED - ACK Led
//pin 5 LED - Send Led



#include <TinyGPS++.h>
#include <SoftwareSerial.h>

static const int RXPin = 4, TXPin = 3;
static const uint32_t GPSBaud = 9600;

// The TinyGPS++ object
TinyGPSPlus gps;

// The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);

//ORIGINAL CODE____________________________________________________________________
/*
    simple ping-pong test by requesting an ACK from the gateway

    Copyright (C) 2016 Congduc Pham, University of Pau, France

    This program 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.

    This program 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 the program.  If not, see <http://www.gnu.org/licenses/>.

 *****************************************************************************
   last update: Nov. 16th by C. Pham
*/
//_________________________________________________
/*
 * Modified by gamecompiler @ 2017
 */
#include <SPI.h>
// Include the SX1272
#include "SX1272.h"

// IMPORTANT
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// please uncomment only 1 choice
//
#define ETSI_EUROPE_REGULATION
//#define FCC_US_REGULATION
//#define SENEGAL_REGULATION
///////////////////////////////////////////////////////////////////////////////////////////////////////////

// IMPORTANT
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// uncomment if your radio is an HopeRF RFM92W, HopeRF RFM95W, Modtronix inAir9B, NiceRF1276
// or you known from the circuit diagram that output use the PABOOST line instead of the RFO line
//#define PABOOST
///////////////////////////////////////////////////////////////////////////////////////////////////////////

// IMPORTANT
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// please uncomment only 1 choice
//#define BAND868
//#define BAND900
#define BAND433
///////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifdef ETSI_EUROPE_REGULATION
#define MAX_DBM 14
#elif defined SENEGAL_REGULATION
#define MAX_DBM 10
#elif defined FCC_US_REGULATION
#define MAX_DBM 14
#endif

#ifdef BAND868
#ifdef SENEGAL_REGULATION
const uint32_t DEFAULT_CHANNEL = CH_04_868;
#else
const uint32_t DEFAULT_CHANNEL = CH_10_868;
#endif
#elif defined BAND900
const uint32_t DEFAULT_CHANNEL = CH_05_900;
#elif defined BAND433
const uint32_t DEFAULT_CHANNEL = CH_00_433;
#endif

///////////////////////////////////////////////////////////////////
// CHANGE HERE THE LORA MODE, NODE ADDRESS
#define LORAMODE  1
#define node_addr 8
//////////////////////////////////////////////////////////////////

// we wrapped Serial.println to support the Arduino Zero or M0
#if defined __SAMD21G18A__ && not defined ARDUINO_SAMD_FEATHER_M0
#define PRINTLN                   SerialUSB.println("")
#define PRINT_CSTSTR(fmt,param)   SerialUSB.print(F(param))
#define PRINT_STR(fmt,param)      SerialUSB.print(param)
#define PRINT_VALUE(fmt,param)    SerialUSB.print(param)
#define FLUSHOUTPUT               SerialUSB.flush();
#else
#define PRINTLN                   Serial.println("")
#define PRINT_CSTSTR(fmt,param)   Serial.print(F(param))
#define PRINT_STR(fmt,param)      Serial.print(param)
#define PRINT_VALUE(fmt,param)    Serial.print(param)
#define FLUSHOUTPUT               Serial.flush();
#endif

#define DEFAULT_DEST_ADDR 1

uint8_t message[100];

int loraMode = LORAMODE;

void setup()
{
  ss.begin(GPSBaud);
  int e;

  // Open serial communications and wait for port to open:
#if defined __SAMD21G18A__ && not defined ARDUINO_SAMD_FEATHER_M0
  SerialUSB.begin(38400);
#else
  Serial.begin(38400);
#endif

  // Print a start message
  PRINT_CSTSTR("%s", "Simple LoRa ping-pong with the gateway\n");

#ifdef ARDUINO_AVR_PRO
  PRINT_CSTSTR("%s", "Arduino Pro Mini detected\n");
#endif

#ifdef ARDUINO_AVR_NANO
  PRINT_CSTSTR("%s", "Arduino Nano detected\n");
#endif

#ifdef ARDUINO_AVR_MINI
  PRINT_CSTSTR("%s", "Arduino MINI/Nexus detected\n");
#endif

#ifdef __MK20DX256__
  PRINT_CSTSTR("%s", "Teensy31/32 detected\n");
#endif

#ifdef __SAMD21G18A__
  PRINT_CSTSTR("%s", "Arduino M0/Zero detected\n");
#endif

  // Power ON the module
  sx1272.ON();

  // Set transmission mode and print the result
  e = sx1272.setMode(loraMode);
  PRINT_CSTSTR("%s", "Setting Mode: state ");
  PRINT_VALUE("%d", e);
  PRINTLN;

  // enable carrier sense
  sx1272._enableCarrierSense = true;

  // Select frequency channel
  e = sx1272.setChannel(DEFAULT_CHANNEL);
  PRINT_CSTSTR("%s", "Setting Channel: state ");
  PRINT_VALUE("%d", e);
  PRINTLN;

  // Select amplifier line; PABOOST or RFO
#ifdef PABOOST
  sx1272._needPABOOST = true;
  // previous way for setting output power
  // powerLevel='x';
#else
  // previous way for setting output power
  // powerLevel='M';
#endif

  // previous way for setting output power
  // e = sx1272.setPower(powerLevel);

  e = sx1272.setPowerDBM((uint8_t)MAX_DBM);
  PRINT_CSTSTR("%s", "Setting Power: state ");
  PRINT_VALUE("%d", e);
  PRINTLN;

  // Set the node address and print the result
  e = sx1272.setNodeAddress(node_addr);
  PRINT_CSTSTR("%s", "Setting node addr: state ");
  PRINT_VALUE("%d", e);
  PRINTLN;

  // Print a success message
  PRINT_CSTSTR("%s", "SX1272 successfully configured\n");

  delay(500);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
}

long long oldmillis = 0;
int mydelay = 1000;
void loop(void)
{
  uint8_t r_size;
  int e;

  sx1272.CarrierSense();

  sx1272.setPacketType(PKT_TYPE_DATA);



  while (1) {
    while (ss.available()) {
      gps.encode(ss.read()); //Update gps if serial avaible
    }

    /*


      double lat;
      double lng;
      char latBuffer[20];
      char lngBuffer[20];
      char payload[100];

        lat = gps.location.lat();
        lng = gps.location.lng();
        dtostrf(lat, 10, 8, latBuffer);
        dtostrf(lng, 10, 8, lngBuffer);

        sprintf(payload, "%s,%s,%d", latBuffer, lngBuffer, satellites);

    */


    if (millis() >= oldmillis + mydelay) { //Send gps data over LoRa if "its time"
      oldmillis = millis();

      if (gps.location.isValid())  {

        char latBuffer[9];
        char lngBuffer[9];
        double tlat = gps.location.lat();
        double tlng = gps.location.lng();
        dtostrf(tlat, 8, 6, latBuffer);
        dtostrf(tlng, 8, 6, lngBuffer);

        char comp[19];
        for (int i = 0; i < 9; i++) {
          comp[i] = latBuffer[i];
          Serial.print(latBuffer[i]);
        }

        comp[9] = 94; //^

        for (int i = 0; i < 9; i++) {
          comp[i + 10] = lngBuffer[i];
          //Serial.print(lngBuffer[i]);
        }
        Serial.println("_------------");

        Serial.println("----------------");
        Serial.println(gps.location.rawLat().deg, HEX);
        Serial.println(gps.location.lng(), HEX);
        Serial.println("----------------");
        for (int i = 0; i < 32; i++)  {
          //Serial.print(latBuffer[i]);
        }
        Serial.println();
        Serial.println("--------------------");
        //String comp[8] = mybuffer + mybuffer2;
        r_size = sprintf((char*)message, comp);
        PRINT_CSTSTR("%s", "Sending Ping");
        digitalWrite(5, HIGH);
        PRINTLN;

        e = sx1272.sendPacketTimeoutACK(DEFAULT_DEST_ADDR, message, r_size);

        // this is the no-ack version
        // e = sx1272.sendPacketTimeout(DEFAULT_DEST_ADDR, message, r_size);

        PRINT_CSTSTR("%s", "Packet sent, state ");



        PRINT_VALUE("%d", e);
        PRINTLN;

        if (e == 3)
          PRINT_CSTSTR("%s", "No Pong!");

        if (e == 0) {
          PRINT_CSTSTR("%s", "Pong received from gateway!");
          digitalWrite(6, HIGH);
          delay(300);
          digitalWrite(6, LOW);
          digitalWrite(5, LOW);
        }

        PRINTLN;


      }
      else  {
        Serial.println("Location is invalid");
        digitalWrite(6, HIGH);
        digitalWrite(5, HIGH);
        delay(200);
        digitalWrite(6, LOW);
        digitalWrite(5, LOW);

      }
    }


  }
}

Copy link

ghost commented Mar 15, 2017

Are you interessted to make a project in this context together? I want to make a replacement of the old APRS over HAM Radios. LoRa is cheaper and in comparision to RF output got more possible distance.

@nobodyguy
Copy link
Author

Nice, thanks for sharing.
Sure, no problem. What problem does your project solve?
My use case is pretty straightforward:
General GPS tracker as small as possible. My plan is to use it for tracking my dog or bike. GPS should be powered off when I don't need it (and can be enabled via web interface).
I have a working prototype (including web interface where you can see map with position checkpoints and configuration interface for enabling GPS, changing reporting interval, etc.), but I'm struggling with a few weird bugs of local LoRaWAN network (Czech Republic). Right now I'm testing a few Sigfox and old fashioned GPRS modules.

Copy link

ghost commented Mar 17, 2017

Mine is not so different. I also want to make everything small as possible to be able to put it in everything.
(Funny idea to track an animal, maybe i could figure out where my cat goes in the night Oo)

But my main goal is to build dropable sensors to meassure tide information on lakes, temp, salt and water height.
Im really interessted in your web interface! My current progress is just to log the positions to a .gpx file... (With horrible bugs, but you cant expect from a E-ing high quality java code, but im future using aprs solves all my problems because then im using excisting APRS software)

But if we implement APRS we could use Software like SARTrack to collect gps location information, weather, etc. Pls check out http://www.cbaprs.de/

You can simply push data to cbaprs using telnet. Im trying to implement telnet in this project:
https://github.com/CongducPham/LowCostLoRaGw

I think APRS is excactly what we both want.

@Jeannotisintheplace
Copy link

Interesting (and lowtech) project @nobodyguy

Would you be like sharing the circuit connections between board,gps and RNJ2483 as well by any chance?
(I would love to try to replicate to train on ARDUINO).

@nobodyguy
Copy link
Author

nobodyguy commented Jun 15, 2021

Interesting (and lowtech) project @nobodyguy

Would you be like sharing the circuit connections between board,gps and RNJ2483 as well by any chance?
(I would love to try to replicate to train on ARDUINO).

Bonjour Jean, I don't really have the project assembled anymore, but I'm sure it was heavily inspired by these resources: https://www.disk91.com/2016/technology/internet-of-things-technology/simple-lora-gps-tracker-based-on-rn2483-and-l80/
https://github.com/hidnseek/hidnseek
https://github.com/ttnmapper/ttntrackernode

@Jeannotisintheplace
Copy link

Děkuji !

I start reading those links with much interest.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment