Skip to content

Instantly share code, notes, and snippets.

@jones2126
Created February 8, 2024 17:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jones2126/c59a4c7479501aa3b7dcf47c0a9d0b3e to your computer and use it in GitHub Desktop.
Save jones2126/c59a4c7479501aa3b7dcf47c0a9d0b3e to your computer and use it in GitHub Desktop.
Sending lat, lon and altitude data from #BESTPOS and $GPGGA messages to Google Sheet in order to survey a site (i.e. average the position over time) so I have use the 'fix position' statement when configuring my Base RTK GPS that sends correction data.
#include <HardwareSerial.h>
#include <WiFi.h>
#include <HTTPClient.h>
// WiFi credentials
const char* ssid = "Pixel_1";
const char* password = "Taco1234";
// Google_Deployment_ID = "AKfycbwZv_vHVI9Hw67vYj609zprKJmdVEuHyH37Mg3VCzo-2lPU9Zaz-dHLp1P7RRAgSDMb"
const char* googleURLPrefix = "https://script.google.com/macros/s/AKfycbwZv_vHVI9Hw67vYj609zprKJmdVEuHyH37Mg3VCzo-2lPU9Zaz-dHLp1P7RRAgSDMb/exec";
HardwareSerial GPS(2); // Use UART 2 for GPS
String gpsMessage = ""; // Global variables to hold gps messages
String messageType = "";
#define maxCommas 30 // 30 selected because it is 1 more than the expected max for BESTPOS sentence
int commaIndices[maxCommas] = {}; // Array to hold indices of commas and semicolon
int commaCount = 0;
unsigned long lastAverageTime = 0;
//const long averageInterval = 600000; // Interval for averaging (10 minutes in milliseconds)
const long averageInterval = 60000; // Interval for averaging (1 minutes in milliseconds) 60 seconds/minute X 1000 milliseconds/second=60,000 milliseconds
// Variables for averaging GPS data
double accumulatedBESTPOSLat = 0, accumulatedBESTPOSLon = 0, accumulatedBESTPOSAlt = 0;
double accumulatedGPGGALat = 0, accumulatedGPGGALon = 0, accumulatedGPGGAAlt = 0;
double avgBESTPOSLat = 0, avgBESTPOSLon = 0, avgBESTPOSAlt = 0, avgGPGGALat = 0, avgGPGGALon = 0, avgGPGGAAlt = 0;
int dataCountBESTPOS = 0, dataCountGPGGA = 0;
double lat = 0.0, lon = 0.0, alt = 0.0;
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("WiFi connected");
GPS.begin(115200, SERIAL_8N1, 16, 17); // RX, TX pins for UART2
}
void loop() {
if (GPS.available()) {
gpsMessage = GPS.readStringUntil('\n');
parseGPSData(); // Parse and accumulate data for averaging
}
// Based on interval setting, calculate and log averages, then reset
if (millis() - lastAverageTime >= averageInterval) {
if (dataCountBESTPOS > 0) {
calculateAndLogAverages();
lastAverageTime = millis();
}
}
}
void parseGPSData() {
// Print the raw GPS data string
Serial.print("Raw GPS Data: ");
Serial.println(gpsMessage);
// Find the position of the commas and store them in an array to facilitate parsing
for (unsigned int i = 0; i < gpsMessage.length(); i++) {
if (gpsMessage[i] == ',') {
if (commaCount > (maxCommas-1)) {
Serial.println("Error: Too many commas in the message.");
return; // Early return to avoid processing incorrect data
}
commaIndices[commaCount] = i;
commaCount += 1;
}
}
messageType = gpsMessage.substring(0, commaIndices[0]);
Serial.print("messageType: "); Serial.print(messageType); Serial.print(" commaCount: "); Serial.println(commaCount);
if (messageType == "#BESTPOSA") {
parseBESTPOSA();
} else if (messageType == "$GPGGA") {
parseGPGGA();
} else {
Serial.print("Error: Unexpected message type. Raw sentence, ("); Serial.print(gpsMessage); Serial.println(")");
}
commaCount = 0; commaIndices[maxCommas] = {};
}
void parseBESTPOSA() {
// Comma locations (start and end): Latitude(11-12), Longitude(12-13), Altitude(13-14)
if (commaCount == 29) { // Confirm we have the expected number of commas
lat = gpsMessage.substring(commaIndices[10] + 1, commaIndices[11]).toDouble();
lon = gpsMessage.substring(commaIndices[11] + 1, commaIndices[12]).toDouble();
String altStr = gpsMessage.substring(commaIndices[12] + 1, commaIndices[13]);
alt = altStr.toDouble();
accumulatedBESTPOSLat += lat;
accumulatedBESTPOSLon += lon;
accumulatedBESTPOSAlt += alt;
dataCountBESTPOS++;
} else {
Serial.print("Error: expected 29 commas, commaCount: "); Serial.println(commaCount);
}
}
void parseGPGGA() {
// Comma locations (start and end): Latitude(2-3), Latitude Direction(3-4), Longitude(4-5), Longitude Direction(5-6), Altitude(9-10)
if (commaCount == 14) { // Confirm we have expected number of commas
lat = convertNMEAToDecimal(gpsMessage.substring(commaIndices[1] + 1, commaIndices[2]), gpsMessage.substring(commaIndices[2] + 1, commaIndices[3]));
lon = convertNMEAToDecimal(gpsMessage.substring(commaIndices[3] + 1, commaIndices[4]), gpsMessage.substring(commaIndices[4] + 1, commaIndices[5]));
String altStr = gpsMessage.substring(commaIndices[8] + 1, commaIndices[9]);
alt = altStr.toDouble();
accumulatedGPGGALat += lat;
accumulatedGPGGALon += lon;
accumulatedGPGGAAlt += alt;
dataCountGPGGA++;
} else {
Serial.print("Error: expected 14 commas, commaCount: "); Serial.println(commaCount);
}
}
double convertNMEAToDecimal(String value, String direction) {
// Determine the number of digits in the degrees based on the length of the string and the direction
// int degreeLength = (condition) ? value_if_true : value_if_false;
int degreeLength = (direction == "N" || direction == "S") ? 2 : 3;
double degrees = value.substring(0, degreeLength).toDouble();
double minutes = value.substring(degreeLength).toDouble();
double decimal = degrees + (minutes / 60);
if (direction == "S" || direction == "W") {
decimal = -decimal;
}
return decimal;
}
void calculateAndLogAverages() {
avgBESTPOSLat = accumulatedBESTPOSLat / dataCountBESTPOS;
avgBESTPOSLon = accumulatedBESTPOSLon / dataCountBESTPOS;
avgBESTPOSAlt = accumulatedBESTPOSAlt / dataCountBESTPOS;
accumulatedBESTPOSLat = 0; accumulatedBESTPOSLon = 0; accumulatedBESTPOSAlt = 0; dataCountBESTPOS = 0;
Serial.printf("Sending BESTPOS Averages: Lat=%.10f, Lon=%.10f, Alt=%.3f\n", avgBESTPOSLat, avgBESTPOSLon, avgBESTPOSAlt);
avgGPGGALat = accumulatedGPGGALat / dataCountGPGGA;
avgGPGGALon = accumulatedGPGGALon / dataCountGPGGA;
avgGPGGAAlt = accumulatedGPGGAAlt / dataCountGPGGA;
accumulatedGPGGALat = 0; accumulatedGPGGALon = 0; accumulatedGPGGAAlt = 0; dataCountGPGGA = 0;
Serial.printf("Sending GPGGA Averages: Lat=%.10f, Lon=%.10f, Alt=%.3f\n", avgGPGGALat, avgGPGGALon, avgGPGGAAlt);
// Send to Google Sheets
sendToGoogleSheets(); // Formulate a URL with data and send to Google Sheets
}
void sendToGoogleSheets() {
if (WiFi.status() == WL_CONNECTED) {
char url[512]; // The current message is 246 characters
snprintf(url, sizeof(url),
"%s?BESTPOSLat=%.10f&BESTPOSLon=%.10f&BESTPOSAlt=%.3f&GPGGALat=%.10f&GPGGALon=%.10f&GPGGAAlt=%.3f",
googleURLPrefix, avgBESTPOSLat, avgBESTPOSLon, avgBESTPOSAlt, avgGPGGALat, avgGPGGALon, avgGPGGAAlt);
HTTPClient http;
size_t urlLength = strlen(url);
Serial.print("URL Length: "); Serial.println(urlLength);
Serial.print("url being sent: "); Serial.println(url);
http.begin(url); // Start the connection
int httpCode = http.GET(); // Send the HTTP GET request
String httpResponse = http.getString(); // Get the request response
if (httpCode == 200) {
Serial.println("Average data sent successfully to Google Sheets.");
} else if (httpCode == 302) {
Serial.println("Warning: Redirection Status code: " + String(httpCode));
Serial.println("Response: " + httpResponse);
} else {
Serial.println("Error: Unexpected Status code: " + String(httpCode));
Serial.println("Response: " + httpResponse);
}
http.end(); // Close connection
} else {
Serial.println("Error: WiFi not connected - No update to Google Sheet");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment