From 6e8a9d24bbe9276628028c4768ce98f7beafce2c Mon Sep 17 00:00:00 2001 From: jl190 <jl190@illinois.edu> Date: Thu, 9 Feb 2023 13:25:52 -0600 Subject: [PATCH] Upload New File --- Homework 3/Question 1 Proof/GPS_test_code.ino | 1189 +++++++++++++++++ 1 file changed, 1189 insertions(+) create mode 100644 Homework 3/Question 1 Proof/GPS_test_code.ino diff --git a/Homework 3/Question 1 Proof/GPS_test_code.ino b/Homework 3/Question 1 Proof/GPS_test_code.ino new file mode 100644 index 0000000..02fbb7c --- /dev/null +++ b/Homework 3/Question 1 Proof/GPS_test_code.ino @@ -0,0 +1,1189 @@ +#include <Adafruit_GPS.h> + + /*************************************************** + This file is GPS_p398dlp.ino + + Read from the Adaruit GPS breakout board. + + To use this: see the functions setup and loop, and replace things + there with your own code. + + + You'll want to do Tools -> Serial Monitor, then set the baud rate to 115,200 + if you want to stream GPS data to the serial monitor window. If you only set + the baud rate to 9600 it'll be too slow, and interfere with data transmission + from the GPS device to the Arduino processor. + + Also make sure that Tools -> Port is set to the Arduino's port. + + We are interested in two kinds of "sentences" holding GPS navigational data: one + begins with $GPRMC, which holds "Recommended minimum specific GPS/Transit data." + The other begins with $GPGGA, which holds "Global Positioning System Fix Data." + Both include latitude and longitude. See a description of the formats, below. + + Here is the $GPRMC format: + 0 - 5 $GPRMC + 7 - 8 hour, UTC + 9 - 10 minutes, UTC + 11 - 12 seconds, UTC + 14 - 16 milliseconds + 18 A for navigation data OK, V for navigation data not present. + 20 - 28 latitude x 100, in degrees; F9.4 format + 30 N or S + 32 - 41 longitude x 100, in degrees; F10.4 format + 43 E or W + next speed over the ground, in knots; floating point, 5 characters + (data between 7th and 8th commas) + next course direction, degrees, floating point + (data between 8th and 9th commas) + other stuff too. Two examples, the first with navigation information, the second without: + $GPRMC,135228.000,A,4006.9605,N,08815.4528,W,0.44,336.27,090618,,,A*71 + $GPRMC,135217.000,V,,,,,0.74,222.59,090618,,,N*45 + 1 2 3 4 5 6 7 8 + 012345678901234567890123456789012345678901234567890123456789012345678901234567890 + + Here is the $GPGGA format: + 0 - 5 $GPGGA + 7 - 8 hour, UTC + 9 - 10 minutes, UTC + 11 - 16 seconds, UTC, F6.3 format + 18 - 26 latitude x 100, in degrees; F9.4 format + 28 N or S + 30 - 39 longitude x 100, in degrees; F10.4 format + 41 E or W + 43 fix quality: 0 for no fix, 1 for GPS, 2 for DGPS. + 45 - 46 number of satellites + 48 - 51 horizontal dilution of precision + 53 - 57 altitude above sea level + 59 M for altitude in meters + other stuff too. Two examples, the first with navigation information, the second without: + $GPGGA,135224.000,4006.9611,N,08815.4523,W,1,07,1.11,211.7,M,-33.9,M,,*53 + $GPGGA,135217.000,,,,,0,07,,,M,,M,,*7C + 1 2 3 4 5 6 7 8 + 012345678901234567890123456789012345678901234567890123456789012345678901234567890 + + Set GPSECHO1 and GPSECHO2 to 'false' to turn off echoing the GPS data to the Serial console + that are useful for debugging. Set GPSECHO3 to 'false' to prevent final navigation result + from being printed to monitor. Set one or more to 'true' if you want to debug and listen + to the raw GPS sentences. GPSECHO1 yields more verbose echoing than GPSECHO2. + + George Gollin + University of Illinois + September 2018 + +***************************************************/ + +////////////////////////////////////////////////////////////////////// +/////////////////////// global parameters //////////////////////////// +////////////////////////////////////////////////////////////////////// + +/////////////////////////// LCD parameters /////////////////////////// + +#include <LiquidCrystal.h> + +// The LCD is a GlobalFontz 16 x 2 device. + +// initialize the LCD library by associating LCD interface pins +// with the arduino pins to which they are connected. first define +// the pin numbers: reset (rs), enable (en), etc. +const int rs = 12, en = 11, d4 = 36, d5 = 34, d6 = 32, d7 = 30; + +// instantiate an LCD object named "lcd" now. We can use its class +// functions to do good stuff, e.g. lcd.print("the text"). +LiquidCrystal lcd(rs, en, d4, d5, d6, d7); + +// total number of characters in one line of the LCD display +#define LCDWIDTH 16 + +//////////////////////////// keypad parameters //////////////////////////// + +#include <Keypad.h> + +// keypad has four rows and three columns. pushing one key connects together +// the corresponding row and column pins. + +const byte ROWS = 4; +const byte COLS = 3; + +// what they keys actually stand for: +char keys[ROWS][COLS] = { +{'1','2','3'}, +{'4','5','6'}, +{'7','8','9'}, +{'*','0','#'} +}; + +// I am using an Arduino Mega 2560 with a number of breakout boards. +// I have the following pin assignments in order to allow the column +// pins to generate interrupts, if I should decide to take awareness of +// the keypad this way. Note that there might be complicated interactions +// between interrupts used to read the microphone's ADC channel and keypad +// interrupts. + +// Arduino pins looking at the three column pins: +byte colPins[COLS] = {2, 3, 18}; +// Arduino pins looking at the four row pins: +byte rowPins[ROWS] = {31, 33, 35, 37}; + +// instantiate a keypad object. +Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); + +// character returned when I query the keypad (might be empty) +char query_keypad_char = NO_KEY; + +// last non-null character read from keypad +char last_key_char = NO_KEY; + +////////////////////////// DS3231 real time clock parameters //////////////////////// + +// this is an I2C device on an Adafruit breakout board. It is a cute little thing +// with a backup battery. + +#include "RTClib.h" + +// instantiate a real time clock object named "rtc": +RTC_DS3231 rtc; + +// names of the days of the week: +char daysOfTheWeek[7][4] = + {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + +// declare type for a few RTC-related global variables +int RTC_hour, RTC_minute, RTC_second; +int RTC_year, RTC_month, RTC_day_of_month; + + +// have we already set the RTC clock from the GPS? +bool already_set_RTC_from_GPS; + +/////////////////////////////// GPS parameters ///////////////////////////// + +// Set GPSECHO1 and GPSECHO2 to 'false' to turn off echoing the GPS data to the Serial console +// that are useful for debugging. Set GPSECHO3 to 'false' to prevent final navigation result +// from being printed to monitor. Set one or more to 'true' if you want to debug and listen +//to the raw GPS sentences. GPSECHO1 yields more verbose echoing than GPSECHO2. +#define GPSECHO1 false +#define GPSECHO2 false +#define GPSECHO3 false + +// get the header file in which lots of things are defined: +#include <Adafruit_GPS.h> + +// also define some more stuff relating to update rates. See +// https://blogs.fsfe.org/t.kandler/2013/11/17/set-gps-update- +// rate-on-arduino-uno-adafruit-ultimate-gps-logger-shield/ +#define PMTK_SET_NMEA_UPDATE_10SEC "$PMTK220,10000*2F" +#define PMTK_SET_NMEA_UPDATE_5SEC "$PMTK220,5000*2F" +#define PMTK_SET_NMEA_UPDATE_1HZ "$PMTK220,1000*1F" +#define PMTK_SET_NMEA_UPDATE_2HZ "$PMTK220,500*2B" +#define PMTK_SET_NMEA_UPDATE_5HZ "$PMTK220,200*2C" + +// let's use the Arduino's second serial port to communicate with the GPS device. +#define GPSSerial Serial2 + +// Connect to the GPS via the Arduino's hardware port +Adafruit_GPS GPS(&GPSSerial); + +// we don't expect a valid GPS "sentence" to be longer than this... +#define GPSMAXLENGTH 120 +// or shorter than this: +#define GPSMINLENGTH 55 + +// last sentence read from the GPS: +char* GPS_sentence; + +// we'll also want to convert the character array to a string for convenience +String GPS_sentence_string; + +String GPS_command; + +// pointers into parts of a GPRMC GPS data sentence: + +const int GPRMC_hour_index1 = 8; +const int GPRMC_hour_index2 = GPRMC_hour_index1 + 2; + +const int GPRMC_minutes_index1 = GPRMC_hour_index2; +const int GPRMC_minutes_index2 = GPRMC_minutes_index1 + 2; + +const int GPRMC_seconds_index1 = GPRMC_minutes_index2; +const int GPRMC_seconds_index2 = GPRMC_seconds_index1 + 2; + +const int GPRMC_milliseconds_index1 = GPRMC_seconds_index2 + 1; // skip the decimal point +const int GPRMC_milliseconds_index2 = GPRMC_milliseconds_index1 + 3; + +const int GPRMC_AV_code_index1 = 19; +const int GPRMC_AV_code_index2 = GPRMC_AV_code_index1 + 1; + +const int GPRMC_latitude_1_index1 = 21; +const int GPRMC_latitude_1_index2 = GPRMC_latitude_1_index1 + 4; + +const int GPRMC_latitude_2_index1 = GPRMC_latitude_1_index2 + 1; // skip the decimal point +const int GPRMC_latitude_2_index2 = GPRMC_latitude_2_index1 + 4; + +const int GPRMC_latitude_NS_index1 = 31; +const int GPRMC_latitude_NS_index2 = GPRMC_latitude_NS_index1 + 1; + +const int GPRMC_longitude_1_index1 = 33; +const int GPRMC_longitude_1_index2 = GPRMC_longitude_1_index1 + 5; // 0 - 180 so we need an extra digit + +const int GPRMC_longitude_2_index1 = GPRMC_longitude_1_index2 + 1; // skip the decimal point +const int GPRMC_longitude_2_index2 = GPRMC_longitude_2_index1 + 4; + +const int GPRMC_longitude_EW_index1 = 44; +const int GPRMC_longitude_EW_index2 = GPRMC_longitude_EW_index1 + 1; + +// pointers into a GPGGA GPS data sentence: + +const int GPGGA_hour_index1 = 8; +const int GPGGA_hour_index2 = GPGGA_hour_index1 + 2; + +const int GPGGA_minutes_index1 = GPGGA_hour_index2; +const int GPGGA_minutes_index2 = GPGGA_minutes_index1 + 2; + +const int GPGGA_seconds_index1 = GPGGA_minutes_index2; +const int GPGGA_seconds_index2 = GPGGA_seconds_index1 + 2; + +const int GPGGA_milliseconds_index1 = GPGGA_seconds_index2 + 1; // skip the decimal point +const int GPGGA_milliseconds_index2 = GPGGA_milliseconds_index1 + 3; + +const int GPGGA_latitude_1_index1 = 19; +const int GPGGA_latitude_1_index2 = GPGGA_latitude_1_index1 + 4; + +const int GPGGA_latitude_2_index1 = GPGGA_latitude_1_index2 + 1; // skip the decimal point +const int GPGGA_latitude_2_index2 = GPGGA_latitude_2_index1 + 4; + +const int GPGGA_latitude_NS_index1 = 29; +const int GPGGA_latitude_NS_index2 = GPGGA_latitude_NS_index1 + 1; + +const int GPGGA_longitude_1_index1 = 31; +const int GPGGA_longitude_1_index2 = GPGGA_longitude_1_index1 + 5; // 0 - 180 so we need an extra digit + +const int GPGGA_longitude_2_index1 = GPGGA_longitude_1_index2 + 1; // skip the decimal point +const int GPGGA_longitude_2_index2 = GPGGA_longitude_2_index1 + 4; + +const int GPGGA_longitude_EW_index1 = 42; +const int GPGGA_longitude_EW_index2 = GPGGA_longitude_EW_index1 + 1; + +const int GPGGA_fix_quality_index1 = 44; +const int GPGGA_fix_quality_index2 = GPGGA_fix_quality_index1 + 1; + +const int GPGGA_satellites_index1 = 46; +const int GPGGA_satellites_index2 = GPGGA_satellites_index1 + 2; + +// keep track of how many times we've read a character from the GPS device. +long GPS_char_reads = 0; + +// bail out if we exceed the following number of attempts. when set to 1,000,000 this corresponds +// to about 6 seconds. we need to do this to keep an unresponsive GPS device from hanging the program. +const long GPS_char_reads_maximum = 1000000; + +// define some of the (self-explanatory) GPS data variables. Times/dates are UTC. +String GPS_hour_string; +String GPS_minutes_string; +String GPS_seconds_string; +String GPS_milliseconds_string; +int GPS_hour; +int GPS_minutes; +int GPS_seconds; +int GPS_milliseconds; + +// this one tells us about data validity: A is good, V is invalid. +String GPS_AV_code_string; + +// latitude data +String GPS_latitude_1_string; +String GPS_latitude_2_string; +String GPS_latitude_NS_string; +int GPS_latitude_1; +int GPS_latitude_2; + +// longitude data +String GPS_longitude_1_string; +String GPS_longitude_2_string; +String GPS_longitude_EW_string; +int GPS_longitude_1; +int GPS_longitude_2; + +// velocity information; speed is in knots! +String GPS_speed_knots_string; +String GPS_direction_string; +float GPS_speed_knots; +float GPS_direction; + +String GPS_date_string; + +String GPS_fix_quality_string; +String GPS_satellites_string; +int GPS_fix_quality; +int GPS_satellites; + +String GPS_altitude_string; +float GPS_altitude; + +////////////////////////////////////////////////////////////////////// +//////////////////// end of global parameters //////////////////////// +////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////// +///////////////////////// setup function ///////////////////////////// +////////////////////////////////////////////////////////////////////// + +void setup() +{ + + // setup is run once, immediately after the program is downloaded into + // the Arduino. + + /////////////////// serial port for serial monitor window /////////////////// + + // set to 115,200 baud so that we don't cause problems associated with reading + // and reporting GPS data. + Serial.begin(115200); + + /////////////////////////// LCD setup //////////////////////////// + + // set up the LCD's number of columns and rows, then print a message: + lcd.begin(16, 2); + LCD_message("P398DLP GPS data", "reader/parser "); + + // delay a bit so I have time to see the display. + delay(1000); + +/////////////////////////// DS3231 real time clock setup ///////////////////////// + + // turn on the RTC and check that it is talking to us. + + if (! rtc.begin()) { + + // uh oh, it's not talking to us. + LCD_message("DS3231 RTC", "unresponsive"); + // delay 5 seconds so that user can read the display + delay(5000); + + } else { + + if (rtc.lostPower()) { + + LCD_message("DS3231 RTC lost", "power. Set to..."); + + // Set the RTC with an explicit date & time: September 1, 1988, 7:37:00 am + rtc.adjust(DateTime(1988, 9, 1, 7, 37, 0)); + + } + } + + // set flag that we haven't already set the RTC clock from the GPS + already_set_RTC_from_GPS = false; + + // ask user for year, month, day of the month. see + // https://www.arduino.cc/en/Tutorial.StringToIntExample + + Serial.println("Please enter the year (e.g. 2018) now."); + + String inString = ""; // string to hold input + int inChar = 12345; + + // did we NOT just get a new line character? + while (inChar != '\n' and inChar != '\r') { + + // see if there's any new input. + while (Serial.available() > 0) { + + // read the input character + inChar = Serial.read(); + + if (isDigit(inChar)) { + // convert the incoming byte to a char and add it to the string: + inString += (char)inChar; + } + + // if you get a newline, print the string, then the string's value: + if (inChar == '\n' or inChar == '\r') { + RTC_year = inString.toInt(); + // clear the string for new input: + inString = ""; + } + } + } + + Serial.println("Please enter the month (e.g. 1 for January) now."); + + inString = ""; // string to hold input + inChar = 12345; + + // did we NOT just get a new line character? + while (inChar != '\n' and inChar != '\r') { + + // see if there's any new input. + while (Serial.available() > 0) { + + // read the input character + inChar = Serial.read(); + + if (isDigit(inChar)) { + // convert the incoming byte to a char and add it to the string: + inString += (char)inChar; + } + + // if you get a newline, print the string, then the string's value: + if (inChar == '\n' or inChar == '\r') { + RTC_month = inString.toInt(); + // clear the string for new input: + inString = ""; + } + } + } + + Serial.println("Please enter the day of the month (e.g. 1 for January 1st) now."); + + inString = ""; // string to hold input + inChar = 12345; + + // did we NOT just get a new line character? + while (inChar != '\n' and inChar != '\r') { + + // see if there's any new input. + while (Serial.available() > 0) { + + // read the input character + inChar = Serial.read(); + + if (isDigit(inChar)) { + // convert the incoming byte to a char and add it to the string: + inString += (char)inChar; + } + + // if you get a newline, print the string, then the string's value: + if (inChar == '\n' or inChar == '\r') { + RTC_day_of_month = inString.toInt(); + // clear the string for new input: + inString = ""; + } + } + } + + Serial.print("you've entered the year, month, day as "); Serial.print(RTC_year); Serial.print("/"); + Serial.print(RTC_month); Serial.print("/"); + Serial.println(RTC_day_of_month); + +////////////////////////////////// GPS setup ////////////////////////////// + + // If you want to see a detailed report of the GPS information, open a serial + // monitor window by going to the Tools -> Serial Monitor menu, then checking + // the Autoscroll box at the bottom left of the window, and setting the baud rate + // to 115200 baud. + + // once we've set up the GPS it will free-run: it has its own built-in microprocessor. + + // 9600 NMEA is the default communication and baud rate for Adafruit MTK GPS units. + // NMEA is "National Marine Electronics Association." + // Note that this serial communication path is different from the one driving the serial + // monitor window on your laptop, which should be running at 115,200 baud. + GPS.begin(9600); + + // turn on RMC (recommended minimum) and GGA (fix data, including altitude) + GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); + + // uncomment this line to turn on only the "minimum recommended" data: + // GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY); + + // Set the update rate to once per second. Faster than this might make it hard for + // the serial communication line to keep up. You'll want to check that the + // faster read rates work reliably for you before using them. + + // what the heck... let's try 5 Hz. + //GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); + // GPS.sendCommand(PMTK_SET_NMEA_UPDATE_2HZ); + GPS.sendCommand(PMTK_SET_NMEA_UPDATE_5HZ); + // GPS.sendCommand(PMTK_SET_NMEA_UPDATE_10HZ); + + // Request updates on antenna status, comment out to keep quiet. + // GPS.sendCommand(PGCMD_ANTENNA); + + // Ask for firmware version, write this to the serial monitor. Comment out to keep quiet. + // GPSSerial.println(PMTK_Q_RELEASE); + +///////////////////////////////////////////////////////////////////////// + +} + + +////////////////////////////////////////////////////////////////////// +//////////////////////////// loop function /////////////////////////// +////////////////////////////////////////////////////////////////////// + +void loop() +{ + + // loop function is executed repeatedly after the setup function finishes. + + // request GPS position information + GPS_query(); + + // now print some of it: the data are stored in global variables and can + // be accessed here. variable names are self explanatory. + Serial.print("\nGPS_hour (UTC) = "); Serial.print(GPS_hour); + Serial.print(" GPS_minutes = "); Serial.print(GPS_minutes); + Serial.print(" GPS_seconds = "); Serial.print(GPS_seconds); + Serial.print(" GPS_milliseconds = "); Serial.println(GPS_milliseconds); + + Serial.print("100 x lattitude and longitude (degrees): "); + Serial.print(GPS_latitude_1_string); Serial.print("."); Serial.print(GPS_latitude_2_string); + Serial.print(GPS_latitude_NS_string); Serial.print(" "); + Serial.print(GPS_longitude_1_string); Serial.print("."); Serial.print(GPS_longitude_2_string); + Serial.println(GPS_longitude_EW_string); + + Serial.print("GPS_speed_knots = "); Serial.print(GPS_speed_knots); + Serial.print(" GPS speed, mph = "); Serial.print(GPS_speed_knots * 1.15078); + Serial.print(" direction (degrees) = "); Serial.println(GPS_direction); + + // Serial.print("try GPS.now... "); Serial.println(GPS.now); + + delay(500); + + // if we have GPS data and haven't yet set the RTC, set the clock now. + if( (GPS_hour != 0 or GPS_minutes != 0 or GPS_seconds != 0 or GPS_milliseconds != 0) + and !already_set_RTC_from_GPS) { + + // we have something coming in from the GPS, and haven't yet set the real time + // clock, so set the RTC now using the last data sentence from the GPS. This isn't + // the most accurate way to do it, but it ought to get us within a second of the + // correct time. + + // To set the RTC with an explicit date & time: September 1, 1988, 7:37:00 am do this: + // rtc.adjust(DateTime(1988, 9, 1, 7, 37, 0)); + + rtc.adjust(DateTime(RTC_year, RTC_month, RTC_day_of_month, GPS_hour, GPS_minutes, GPS_seconds)); + already_set_RTC_from_GPS = true; + + } + + // talk to the realtime clock and print its information. note that unless you've + // synchronized the RTC and GPS, they won't necessarily agree. + DS3231_query(); + + Serial.print("RTC_hour = "); Serial.print(RTC_hour); + Serial.print(" RTC_minute = "); Serial.print(RTC_minute); + Serial.print(" RTC_second = "); Serial.println(RTC_second); + + delay(500); + +} + + +////////////////////////////////////////////////////////////////////// +////////////////////////// LCD_message function ////////////////////// +////////////////////////////////////////////////////////////////////// + +void LCD_message(String line1, String line2) +{ + // write two lines (of 16 characters each, maximum) to the LCD display. + // I assume an object named "lcd" has been created already, has been + // initialized in setup, and is global. + + // set the cursor to the beginning of the first line, clear the line, then write. + lcd.setCursor(0, 0); + lcd.print(" "); + lcd.setCursor(0, 0); + lcd.print(line1); + + // now do the next line. + lcd.setCursor(0, 1); + lcd.print(" "); + lcd.setCursor(0, 1); + lcd.print(line2); + + return; +} + + +////////////////////////////////////////////////////////////////////// +////////////////////// DS3231_query function ///////////////////////// +////////////////////////////////////////////////////////////////////// + +void DS3231_query() +{ + // read from the DS3231 real time clock. + + // these were already declared as global variables. + // int RTC_minute, RTC_second; + + // we have already instantiated the device as an object named "now" so we can + // call its class functions. + DateTime now = rtc.now(); + + lcd.setCursor(0, 0); + lcd.print("Realtime clock "); + + // write time information to LCD. Year, etc. first, after setting LCD to second line. + lcd.setCursor(0, 1); + lcd.print(" "); + lcd.setCursor(0, 1); + lcd.print(now.year(), DEC); + lcd.print('/'); + lcd.print(now.month(), DEC); + lcd.print('/'); + lcd.print(now.day(), DEC); + + // delay a second + delay(1000); + + // now display the day of the week and time. + RTC_hour = now.hour(); + RTC_minute = now.minute(); + RTC_second = now.second(); + + lcd.setCursor(0, 1); + lcd.print(" "); + lcd.setCursor(0, 1); + lcd.print(daysOfTheWeek[now.dayOfTheWeek()]); + lcd.print(". "); + + lcd.print(RTC_hour, DEC); + lcd.print(':'); + if (RTC_minute < 10) {lcd.print('0');} + lcd.print(RTC_minute, DEC); + lcd.print(':'); + if (RTC_second < 10) {lcd.print('0');} + lcd.print(RTC_second, DEC); + + // there are other functions available, such as + // now.unixtime(); + // DateTime future (now + TimeSpan(7,12,30,6)); + // future.year(); etc. etc. + +} + + + +////////////////////////////////////////////////////////////////////// +////////////////////// GPS_query function ///////////////////////// +////////////////////////////////////////////////////////////////////// + +int GPS_query() +{ + + // return 0 if we found good GPS navigational data and -1 if not. + + // The GPS device has its own microprocessor and, once we have loaded its parameters, + // free-runs at a 1 Hz sampling rate. We do not trigger its registration of + // latitude and longitude, rather we just read from it the last data record + // it has stored. And we do it one character at a time! + + // I will keep reading from the GPS until I have a complete sentence carrying valid + // navigational data, with a maximum number of reads to prevent the program from hanging. Once + // the GPS reports its updates latitude/longitude information, we'll push this to the + // LCD display, then return to the main loop. + + // zero out (or set to defaults) values returned by the GPS device in case we can't + // get it to respond. + GPS_hour = GPS_minutes = GPS_seconds = GPS_milliseconds = 0; + + GPS_AV_code_string = "V"; + + GPS_latitude_1 = GPS_latitude_2 = 0; + GPS_latitude_NS_string = "x"; + + GPS_longitude_1 = GPS_longitude_2 = 0; + GPS_longitude_EW_string = "y"; + + GPS_speed_knots = 0.; + GPS_direction = 0.; + + GPS_date_string = "000000"; + + GPS_fix_quality = GPS_satellites = 0; + + GPS_altitude = 0.; + + // initialize the number-of-reads counter since we might find ourselves + // with an unparseable data record, or an unresponsive GPS, and want to keep trying. + // This will let me protect against the data logger's program hanging. + GPS_char_reads = 0; + + // set a flag saying we want to keep trying; we'll use this to keep looking for + // useful navigation when the GPS is working fine, but still looking for satellites. + bool keep_trying = true; + + // Stay inside the following loop until we've read a complete GPS sentence with + // good navigational data, or else the loop times out. With GPS_char_reads_maximum + // set to a million this'll take about 6 seconds to time out. + + while (true) { + + // if we get back to this point but the keep_trying flag is false, we'll want + // to declare failure and quit. + + if(!keep_trying) return -1; + + // this gets the last sentence read from GPS and clears a newline flag in the Adafruit + // library code. + GPS_sentence = GPS.lastNMEA(); + + while(GPS_char_reads <= GPS_char_reads_maximum) + { + + // try to read a single character from the GPS device. + char single_GPS_char = GPS.read(); + + // bump the number of times we've tried to read from the GPS. + GPS_char_reads++; + + // now ask if we've received a complete data sentence. If yes, break + // out of this loop. + + if(GPS.newNMEAreceived()) break; + + } + + + // if we hit the limit on the number of character reads we'e tried, print a message and bail out. + if (GPS_char_reads >= GPS_char_reads_maximum) + { + + keep_trying = false; + + Serial.println("GPS navigation data not yet available. Try again later."); + LCD_message("GPS navigation ", "data unavailable"); + + return -1; + + } + + // get the last complete sentence read from GP; this automatically clears a newline flag inside + // the Adafruit library code. + GPS_sentence = GPS.lastNMEA(); + + // convert GPS data sentence from a character array to a string. + GPS_sentence_string = String(GPS_sentence); + + // now do a cursory check that the sentence we've just read is OK. Check that there is only + // one $, as the first character in the sentence, and that there's an asterisk (which commes + // immediately before the checksum). + + // sentence starts with a $? + bool data_OK = GPS_sentence_string.charAt(0) == '$'; + + // sentence contains no other $? The indexOf call will return -1 if $ is not found. + data_OK = data_OK && (GPS_sentence_string.indexOf('$', 2) < 0); + + // now find that asterisk... + data_OK = data_OK && (GPS_sentence_string.indexOf('*', 0) > 0); + + if (GPSECHO1) + { + Serial.println("\n******************\njust received a complete sentence, so parse stuff. Sentence is"); + Serial.println(GPS_sentence_string); + } + + // now parse the GPS sentence. I am only interested in sentences that begin with + // $GPGGA ("GPS fix data") or $GPRMC ("recommended minimum specific GPS/Transit data"). + + if(GPSECHO1) + { + Serial.print("length of GPS_sentence_string just received..."); + Serial.println(GPS_sentence_string.length()); + } + + // now get substring holding the GPS command. Only proceed if it is $GPRMC or $GPGGA. + GPS_command = GPS_sentence_string.substring(0, 6); + + // also trim it to make sure we don't have hidden stuff or white space sneaking in. + GPS_command.trim(); + + if(GPSECHO1) + { + Serial.print("GPS command is "); Serial.println(GPS_command); + } + + // if data_OK is true then we have a good sentence. but we also need the sentence + // to hold navigational data we can use, otherwise we'll want to keep listening. + // we can only work with GPRMC and GPGGA sentences. + + bool command_OK = GPS_command.equals("$GPRMC") || GPS_command.equals("$GPGGA"); + + // if we have a sentence that, upon cursory inspection, is well formatted AND might + // hold navigational data, continue to parse the sentence. If the GPS device + // hasn't found any satellites yet, we'll want to go back to the top of the loop + // to keep trying, rather than declaring defeat and returning. + + ////////////////////////////////////////////////////////////////////// + /////////////////////////// GPRMC sentence /////////////////////////// + ////////////////////////////////////////////////////////////////////// + + if (data_OK && GPS_command.equals("$GPRMC")) + { + + if(GPSECHO2) + { + Serial.print("\nnew GPS sentence: "); Serial.println(GPS_sentence_string); + } + + // parse the time. these are global variables, already declared. + + GPS_hour_string = GPS_sentence_string.substring(GPRMC_hour_index1, GPRMC_hour_index2); + GPS_minutes_string = GPS_sentence_string.substring(GPRMC_minutes_index1, GPRMC_minutes_index2); + GPS_seconds_string = GPS_sentence_string.substring(GPRMC_seconds_index1, GPRMC_seconds_index2); + GPS_milliseconds_string = GPS_sentence_string.substring(GPRMC_milliseconds_index1, + GPRMC_milliseconds_index2); + GPS_AV_code_string = GPS_sentence_string.substring(GPRMC_AV_code_index1, GPRMC_AV_code_index2); + + GPS_hour = GPS_hour_string.toInt(); + GPS_minutes = GPS_minutes_string.toInt(); + GPS_seconds = GPS_seconds_string.toInt(); + GPS_milliseconds = GPS_milliseconds_string.toInt(); + + if(GPSECHO2) + { + Serial.print("Time (UTC) = "); Serial.print(GPS_hour); Serial.print(":"); + Serial.print(GPS_minutes); Serial.print(":"); + Serial.print(GPS_seconds); Serial.print("."); + Serial.println(GPS_milliseconds); + Serial.print("A/V code is "); Serial.println(GPS_AV_code_string); + } + + // now see if the data are valid: we'll expect an "A" as the AV code string. + // We also expect an asterisk two characters from the end. Also check that the sentence + // is at least as long as the minimum length expected. + + data_OK = GPS_AV_code_string == "A"; + + // now look for the asterisk after trimming any trailing whitespace in the GPS sentence. + // the asterisk preceeds the sentence's checksum information, which I won't bother to check. + int asterisk_should_be_here = GPS_sentence_string.length() - 4; + + data_OK = data_OK && (GPS_sentence_string.charAt(asterisk_should_be_here) == '*'); + + if(GPSECHO2) + { + Serial.print("expected asterisk position "); Serial.print(asterisk_should_be_here); + Serial.print(" at that position: "); Serial.println(GPS_sentence_string.charAt(asterisk_should_be_here)); + } + + // now check that the sentence is not too short. + data_OK = data_OK && (GPS_sentence_string.length() >= GPSMINLENGTH); + + if (!data_OK) + { + + keep_trying = true; + + if (GPSECHO1) + { + Serial.print("GPS sentence not good for navigation: "); Serial.println(GPS_sentence_string); + Serial.println("I will keep trying..."); + } + + lcd.setCursor(0, 0); + lcd.print("GPS navigation "); + lcd.setCursor(0, 1); + lcd.print("data not present"); + + } + + // if data are not good, go back to the top of the loop by breaking out of this if block. + // we've already set keep_trying to be true. + + if (!data_OK) break; + + // so far so good, so keep going... + + // now parse latitude + + GPS_latitude_1_string = GPS_sentence_string.substring(GPRMC_latitude_1_index1, + GPRMC_latitude_1_index2); + GPS_latitude_2_string = GPS_sentence_string.substring(GPRMC_latitude_2_index1, + GPRMC_latitude_2_index2); + GPS_latitude_NS_string = GPS_sentence_string.substring(GPRMC_latitude_NS_index1, + GPRMC_latitude_NS_index2); + + GPS_latitude_1 = GPS_latitude_1_string.toInt(); + GPS_latitude_2 = GPS_latitude_2_string.toInt(); + + if(GPSECHO2) + { + Serial.print("Latitude x 100 = "); Serial.print(GPS_latitude_1); Serial.print("."); + Serial.print(GPS_latitude_2); Serial.println(GPS_latitude_NS_string); + } + + // now parse longitude + + GPS_longitude_1_string = GPS_sentence_string.substring(GPRMC_longitude_1_index1, + GPRMC_longitude_1_index2); + GPS_longitude_2_string = GPS_sentence_string.substring(GPRMC_longitude_2_index1, + GPRMC_longitude_2_index2); + GPS_longitude_EW_string = GPS_sentence_string.substring(GPRMC_longitude_EW_index1, + GPRMC_longitude_EW_index2); + + GPS_longitude_1 = GPS_longitude_1_string.toInt(); + GPS_longitude_2 = GPS_longitude_2_string.toInt(); + + if(GPSECHO2) + { + Serial.print("Longitude x 100 = "); Serial.print(GPS_longitude_1); Serial.print("."); + Serial.print(GPS_longitude_2); Serial.println(GPS_longitude_EW_string); + } + + // now parse speed and direction. we'll need to locate the 7th and 8th commas in the + // data sentence to do this. so use the indexOf function to find them. + // it returns -1 if string wasn't found. the number of digits is not uniquely defined + // so we need to find the fields based on the commas separating them from others. + + int comma_A_index = GPRMC_longitude_EW_index2; + int comma_B_index = GPS_sentence_string.indexOf(",", comma_A_index + 1); + int comma_C_index = GPS_sentence_string.indexOf(",", comma_B_index + 1); + + GPS_speed_knots_string = GPS_sentence_string.substring(comma_A_index + 1, comma_B_index); + GPS_direction_string = GPS_sentence_string.substring(comma_B_index + 1, comma_C_index); + + GPS_speed_knots = GPS_speed_knots_string.toFloat(); + GPS_direction = GPS_direction_string.toFloat(); + + if(GPSECHO2) + { + Serial.print("Speed (knots) = "); Serial.println(GPS_speed_knots); + Serial.print("Direction (degrees) = "); Serial.println(GPS_direction); + } + + // now get the (UTC) date, in format DDMMYY, e.g. 080618 for 8 June 2018. + GPS_date_string = GPS_sentence_string.substring(comma_C_index+ + 1, comma_C_index + 7); + + if(GPSECHO2) + { + Serial.print("date, in format ddmmyy = "); Serial.println(GPS_date_string); + } + + // Write message to LCD now. It will look like this (no satellite data in this record): + // Sats: 4006.9539N + // N/A 08815.4431W + + lcd.setCursor(0, 0); + lcd.print("Sats: "); + lcd.setCursor(6, 0); + lcd.print(GPS_latitude_1_string); lcd.print("."); lcd.print(GPS_latitude_2_string); + lcd.print(GPS_latitude_NS_string); + + lcd.setCursor(0, 1); + lcd.print("N/A "); + lcd.setCursor(5, 1); + lcd.print(GPS_longitude_1_string); lcd.print("."); lcd.print(GPS_longitude_2_string); + lcd.print(GPS_longitude_EW_string); + + // print a summary of the data and parsed results: + if(GPSECHO3) + { + Serial.print("GPS sentence: "); Serial.println(GPS_sentence_string); + + Serial.print("Time (UTC) = "); Serial.print(GPS_hour); Serial.print(":"); + Serial.print(GPS_minutes); Serial.print(":"); + Serial.print(GPS_seconds); Serial.print("."); + Serial.println(GPS_milliseconds); + + Serial.print("Latitude x 100 = "); Serial.print(GPS_latitude_1); Serial.print("."); + Serial.print(GPS_latitude_2); Serial.print(" "); Serial.print(GPS_latitude_NS_string); + + Serial.print(" Longitude x 100 = "); Serial.print(GPS_longitude_1); Serial.print("."); + Serial.print(GPS_longitude_2); Serial.print(" "); Serial.println(GPS_longitude_EW_string); + + Serial.print("Speed (knots) = "); Serial.print(GPS_speed_knots); + Serial.print(" Direction (degrees) = "); Serial.println(GPS_direction); + + Serial.println("There is no satellite or altitude information in a GPRMC data sentence."); + + } + + // all done with this sentence, so return. + return 0; + + } // end of "if (data_OK && GPS_command.equals("$GPRMC"))" block + + ////////////////////////////////////////////////////////////////////// + /////////////////////////// GPGGA sentence /////////////////////////// + ////////////////////////////////////////////////////////////////////// + + if (data_OK && GPS_command.equals("$GPGGA")) + { + + if(GPSECHO2) + { + Serial.print("\nnew GPS sentence: "); Serial.println(GPS_sentence_string); + } + + // parse the time + + GPS_hour_string = GPS_sentence_string.substring(GPGGA_hour_index1, GPGGA_hour_index2); + GPS_minutes_string = GPS_sentence_string.substring(GPGGA_minutes_index1, GPGGA_minutes_index2); + GPS_seconds_string = GPS_sentence_string.substring(GPGGA_seconds_index1, GPGGA_seconds_index2); + GPS_milliseconds_string = GPS_sentence_string.substring(GPGGA_milliseconds_index1, + GPGGA_milliseconds_index2); + + GPS_hour = GPS_hour_string.toInt(); + GPS_minutes = GPS_minutes_string.toInt(); + GPS_seconds = GPS_seconds_string.toInt(); + GPS_milliseconds = GPS_milliseconds_string.toInt(); + + if(GPSECHO2) + { + Serial.print("Time (UTC) = "); Serial.print(GPS_hour); Serial.print(":"); + Serial.print(GPS_minutes); Serial.print(":"); + Serial.print(GPS_seconds); Serial.print("."); + Serial.println(GPS_milliseconds); + } + + // now get the fix quality and number of satellites. + + GPS_fix_quality_string = GPS_sentence_string.substring(GPGGA_fix_quality_index1, + GPGGA_fix_quality_index2); + GPS_satellites_string = GPS_sentence_string.substring(GPGGA_satellites_index1, + GPGGA_satellites_index2); + + int GPS_fix_quality = GPS_fix_quality_string.toInt(); + int GPS_satellites = GPS_satellites_string.toInt(); + + if(GPSECHO2) + { + Serial.print("fix quality (1 for GPS, 2 for DGPS) = "); Serial.println(GPS_fix_quality); + Serial.print("number of satellites = "); Serial.println(GPS_satellites); + } + + // now see if the data are valid: we'll expect a fix, and at least three satellites. + + bool data_OK = (GPS_fix_quality > 0) && (GPS_satellites >= 3); + + // now look for the asterisk. + int asterisk_should_be_here = GPS_sentence_string.length() - 4; + + data_OK = data_OK && (GPS_sentence_string.charAt(asterisk_should_be_here) == '*'); + + // now check that the sentence is not too short. + data_OK = data_OK && (GPS_sentence_string.length() >= GPSMINLENGTH); + + if (!data_OK) + { + + keep_trying = true; + + if (GPSECHO1) + { + Serial.print("GPS sentence not good for navigation: "); Serial.println(GPS_sentence_string); + Serial.println("I will keep trying..."); + } + + lcd.setCursor(0, 0); + lcd.print("GPS navigation "); + lcd.setCursor(0, 1); + lcd.print("data not present"); + + } + + // if data are not good, go back to the top of the loop by breaking out of this if block. + + if (!data_OK) break; + + // so far so good, so keep going... + + // now parse latitude + + String GPS_latitude_1_string = GPS_sentence_string.substring(GPGGA_latitude_1_index1, + GPGGA_latitude_1_index2); + String GPS_latitude_2_string = GPS_sentence_string.substring(GPGGA_latitude_2_index1, + GPGGA_latitude_2_index2); + String GPS_latitude_NS_string = GPS_sentence_string.substring(GPGGA_latitude_NS_index1, + GPGGA_latitude_NS_index2); + + int GPS_latitude_1 = GPS_latitude_1_string.toInt(); + int GPS_latitude_2 = GPS_latitude_2_string.toInt(); + + if(GPSECHO2) + { + Serial.print("Latitude x 100 = "); Serial.print(GPS_latitude_1); Serial.print("."); + Serial.print(GPS_latitude_2); Serial.println(GPS_latitude_NS_string); + } + + // now parse longitude + + String GPS_longitude_1_string = GPS_sentence_string.substring(GPGGA_longitude_1_index1, + GPGGA_longitude_1_index2); + String GPS_longitude_2_string = GPS_sentence_string.substring(GPGGA_longitude_2_index1, + GPGGA_longitude_2_index2); + String GPS_longitude_EW_string = GPS_sentence_string.substring(GPGGA_longitude_EW_index1, + GPGGA_longitude_EW_index2); + + int GPS_longitude_1 = GPS_longitude_1_string.toInt(); + int GPS_longitude_2 = GPS_longitude_2_string.toInt(); + + if(GPSECHO2) + { + Serial.print("Longitude x 100 = "); Serial.print(GPS_longitude_1); Serial.print("."); + Serial.print(GPS_longitude_2); Serial.println(GPS_longitude_EW_string); + } + + // let's skip the "horizontal dilution" figure and go straight for the altitude now. + // this begins two fields to the right of the num,ber of satellites so find this + // by counting commas. use the indexOf function to find them. + int comma_A_index = GPS_sentence_string.indexOf(",", GPGGA_satellites_index2 + 1); + int comma_B_index = GPS_sentence_string.indexOf(",", comma_A_index + 1); + + String GPS_altitude_string = GPS_sentence_string.substring(comma_A_index + 1, comma_B_index); + + float GPS_altitude = GPS_altitude_string.toFloat(); + + if(GPSECHO2) + { + Serial.print("Altitude (meters) = "); Serial.println(GPS_altitude); + } + + // Write message to LCD now. It will look like this: + // Sats: 4006.9539N + // 10 08815.4431W + + lcd.setCursor(0, 0); + lcd.print("Sats: "); + lcd.setCursor(6, 0); + lcd.print(GPS_latitude_1_string); lcd.print("."); lcd.print(GPS_latitude_2_string); + lcd.print(GPS_latitude_NS_string); + + lcd.setCursor(0, 1); + lcd.print(" "); + lcd.setCursor(2, 1); + lcd.print(GPS_satellites); + lcd.setCursor(5, 1); + lcd.print(GPS_longitude_1_string); lcd.print("."); lcd.print(GPS_longitude_2_string); + lcd.print(GPS_longitude_EW_string); + + // print a summary of the data and parsed results: + if(GPSECHO3) + { + Serial.print("GPS sentence: "); Serial.println(GPS_sentence_string); + + Serial.print("Time (UTC) = "); Serial.print(GPS_hour); Serial.print(":"); + Serial.print(GPS_minutes); Serial.print(":"); + Serial.print(GPS_seconds); Serial.print("."); + Serial.println(GPS_milliseconds); + + Serial.print("Latitude x 100 = "); Serial.print(GPS_latitude_1); Serial.print("."); + Serial.print(GPS_latitude_2); Serial.print(" "); Serial.print(GPS_latitude_NS_string); + + Serial.print(" Longitude x 100 = "); Serial.print(GPS_longitude_1); Serial.print("."); + Serial.print(GPS_longitude_2); Serial.print(" "); Serial.println(GPS_longitude_EW_string); + + Serial.print("Speed (knots) = "); Serial.print(GPS_speed_knots); + Serial.print(" Direction (degrees) = "); Serial.println(GPS_direction); + + Serial.print("Number of satellites: "); Serial.print(GPS_satellites); + Serial.print(" Altitude (meters): "); Serial.println(GPS_altitude); + + } + + // all done with this sentence, so return. + return 0; + + } // end of "if (data_OK && GPS_command.equals("$GPGGA"))" block + + // we'll fall through to here (instead of returning) when we've read a complete + // sentence, but it doesn't have navigational information (for example, an antenna + // status record). + + } + + } -- GitLab