LoRa GPS Duplex A with ESP32 T-Beam

Updated on 23 September 2022
dev board LilyGO T-Beam
chip ESP32-DOWDQ6
sensor gps
features LoRa duplex
This tutorial is more than 1 year old. If the steps below do not work, then please check the latest versions and the documentations of the individual tools used.

Before starting

Dependancies

Ensure the following dependancies are downloaded and available:

Pre-requisites

Buy the components

Code

Download code lora-duplex-a-gps-esp32-t-beam.ino
#include "src/gps/gps.h"
#include "src/oled/oled.h"
#include "src/lorap2p/lorap2p.h"

#define GPS_ACCURACY 7
#define PERIOD 2000

byte localAddress = 0xAA;
byte peerAddress = 0xBB;

unsigned long localGPSFixMillis;
unsigned long receivedGPSFixMillis;
unsigned long lastKnownHaversineDistanceMillis;

long lastSendTime = 0;
int interval = PERIOD;
long haversine_distance = 0.0;

LatLong localLatlong = {0.0, 0.0};
LatLong lastKnownLocalLatlong = {0.0, 0.0};
LatLong destinationLatlong = {0.0, 0.0};

void setup() {
  Serial.begin(9600);
  Serial.println("Start LoRa GPS duplex");

  initGPS();
  initOLED();
  initLora();

  displayInitOLED();
}

void loop() {
  while (isGPSAvailable()) {
    getLatLong(&localLatlong);
  }

  if (receiveLora(localAddress, &destinationLatlong.latitude, &destinationLatlong.longitude)) {
    receivedGPSFixMillis = millis();
    printStatus("Received", &destinationLatlong, peerAddress, localAddress);
  }

  if (millis() - lastSendTime > interval) {
    if (isGPSValid(&localLatlong)) {

      // If new GPS lat-long is received
      // Then update timestamp
      // Then send to peer LoRa node
      if (!isGPSsameAsLastKnown(&lastKnownLocalLatlong, &localLatlong)) {
        localGPSFixMillis = millis();

        String latlongData = String(localLatlong.latitude, GPS_ACCURACY) + "," + String(localLatlong.longitude, GPS_ACCURACY);
        sendLora(latlongData, localAddress, peerAddress);

        lastKnownLocalLatlong.latitude = localLatlong.latitude;
        lastKnownLocalLatlong.longitude = localLatlong.longitude;
      }

      // Always display on the OLED with relative time ago
      String localMillisStr;
      getReadableTime(localGPSFixMillis, localMillisStr);

      Serial.print("Lat-Long: ");
      Serial.print(localLatlong.latitude, 7);
      Serial.print(",");
      Serial.print(localLatlong.longitude, 7);
      Serial.print(" at ");
      Serial.print(localGPSFixMillis);
      Serial.print("ms. ");
      Serial.print(localMillisStr);
      Serial.println(" ago");

      if (doesBothPeerHaveGPSAtSimilarTime(localGPSFixMillis, receivedGPSFixMillis)) {
        haversine_distance = distance(localLatlong.latitude, localLatlong.longitude, destinationLatlong.latitude, destinationLatlong.longitude);
        Serial.print("Haversine distance: ");
        Serial.print(haversine_distance);
        Serial.println("m.");

        lastKnownHaversineDistanceMillis = millis();
      }

      String peerMillisStr;
      getReadableTime(lastKnownHaversineDistanceMillis, peerMillisStr);

      displayOLED(localLatlong.latitude, localLatlong.longitude, localMillisStr, haversine_distance, peerMillisStr);


      lastSendTime = millis();
      interval = random(1000) + PERIOD;
    }
  }
}

void printStatus(String status, struct LatLong *ll, byte addressA, byte addressB) {
  Serial.print(status + " latlong ");
  Serial.print(ll->latitude, GPS_ACCURACY);
  Serial.print(",");
  Serial.print(ll->longitude, GPS_ACCURACY);
  Serial.print(" from 0x" + String(addressA, HEX));
  Serial.println(" to 0x" + String(addressB, HEX));
}

void getReadableTime(long millisTime, String &readableTime) {
  unsigned long seconds = (millis() - millisTime) / 1000;
  unsigned long minutes = seconds / 60;
  unsigned long hours = minutes / 60;
  unsigned long days = hours / 24;
  millisTime %= 1000;
  seconds %= 60;
  minutes %= 60;
  hours %= 24;

  if (days > 0) {
    readableTime = String(days) + " ";
  }

  if (hours > 0) {
    readableTime += String(hours) + ":";
  }

  if (minutes < 10) {
    readableTime += "0";
  }
  readableTime += String(minutes) + ":";

  if (seconds < 10) {
    readableTime += "0";
  }
  readableTime += String(seconds);
}

Makefile

BOARD?=esp32:esp32:t-beam
PORT?=/dev/cu.SLAB_USBtoUART

.PHONY: default lint all flash clean

default: all flash clean

lint:
	cpplint --extensions=ino --filter=-legal/copyright *.ino

all:
	arduino-cli compile --fqbn $(BOARD) ./

flash:
	arduino-cli upload -p $(PORT) --fqbn $(BOARD)

clean:
	rm -r build

Prototype

A photo of the actual setup.

LoRa GPS Duplex A with ESP32 T-Beam prototype

Serial console

Serial output from the firmware.

LoRa GPS Duplex A with ESP32 T-Beam serial console

Description

This example contains a LoRa duplex code to exchange GPS data between 2 nodes of the ESP32-based T-Beam by Xinyuan LilyGO version T22_V1.1, 20191212. After exchanging the GPS data, it will display its own latitude-longitude as well as the Haversine distance between the 2 notes on the OLED display.

Use this code with duplex b.

byte localAddress = 0xAA;
byte destination = 0xBB;

4 types of information are shown on the OLED display:

  1. Local node’s latitude, longitude pair
  2. Last time since the local node had a GPS fix
  3. Haversine distance with the peer node
  4. Last time since both the local and peer node could get GPS fix around the same time enough to get a valid Haversine distance

References

Watch