diff --git a/Homework 3/.gitkeep b/Homework 3/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Homework 3/Question 1 Proof/.gitkeep b/Homework 3/Question 1 Proof/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Homework 3/Question 1 Proof/DAQ_initial.ino b/Homework 3/Question 1 Proof/DAQ_initial.ino new file mode 100644 index 0000000000000000000000000000000000000000..d7b5fb20434310676036e7a37f11012ad0122baa --- /dev/null +++ b/Homework 3/Question 1 Proof/DAQ_initial.ino @@ -0,0 +1,1012 @@ +/* +Group 1: Jeremy, Pavan, Ayush, Sam +Homework 3: DAQ +9th of Feburary, 2023 + +File Name: DAQ initial + +Goal/Purpose: To have a program that is able to collect and use all of the sensors and devices intended +for our project. In this code, it will read the GPS and RTC for time, then read the BME for temperature +and write it and the name of a picture taken to an SD Card), all while the LCD is displaying updates on +what task is currently being underwent and how many pictures/iterations of the loop have successfully executed. + +We spent around 12 hours total on this project, and referenced previous homeworks, online adafruit/arduino fourms, +and the provided test code on the 371 website. The data graph/mean/standard deviation is found in the excel file. +*/ +#include <Wire.h> +#include <SPI.h> +#include <Adafruit_Sensor.h> +#include "Adafruit_BME680.h" +#include <LiquidCrystal.h> +#include <TimeLib.h> +#include <DS1307RTC.h> +#include <Keypad.h> +#include "SdFat.h" +#include <Adafruit_VC0706.h> +//#include <SD.h> +#include <Adafruit_GPS.h> +#include "RTClib.h" +RTC_DS3231 rtc; +//GPS +#define GPSSerial Serial1 +#define GPSECHO1 false +#define GPSECHO2 false +#define GPSECHO3 false +#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" + +//SNAPSHOT +#if defined(__AVR__) || defined(ESP8266) +#include <SoftwareSerial.h> +SoftwareSerial cameraconnection(69, 3); +#else +#define cameraconnection Serial1 + +#endif +#define chipSelect 53 + +//BME SD +#define BME_SCK 13 +#define BME_MISO 12 +#define BME_MOSI 11 +#define BME_CS 10 +#define SD_CS_PIN SS +#define SEALEVELPRESSURE_HPA (1013.25) +Adafruit_VC0706 cam = Adafruit_VC0706(&cameraconnection); +#define GPSMINLENGTH 55 +#define GPSMAXLENGTH 120 +Adafruit_GPS GPS(&GPSSerial); +char* GPS_sentence; +String GPS_sentence_string; +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; +String GPS_command; +char daysOfTheWeek[7][4] = + {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; +int RTC_hour, RTC_minute, RTC_second; +int RTC_year, RTC_month, RTC_day_of_month; +bool already_set_RTC_from_GPS; + + +SdFat SD_thingy; +File myFiley; + +char filename[ ] = "data.csv"; + +const char *monthName[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; +tmElements_t tm; +Adafruit_BME680 bme; // I2C +const byte ROWS = 4; +const byte COLS = 3; + +char keys[ROWS][COLS] = { +{'1','2','3'}, +{'4','5','6'}, +{'7','8','9'}, +{'*','0','#'} +}; +bool getTime(const char *str) +{ + int Hour, Min, Sec; + + if (sscanf(str, "%d:%d:%d", &Hour, &Min, &Sec) != 3) return false; + tm.Hour = Hour; + tm.Minute = Min; + tm.Second = Sec; + return true; +} + +bool getDate(const char *str) +{ + char Month[12]; + int Day, Year; + uint8_t monthIndex; + + if (sscanf(str, "%s %d %d", Month, &Day, &Year) != 3) return false; + for (monthIndex = 0; monthIndex < 12; monthIndex++) { + if (strcmp(Month, monthName[monthIndex]) == 0) break; + } + if (monthIndex >= 12) return false; + tm.Day = Day; + tm.Month = monthIndex + 1; + tm.Year = CalendarYrToTm(Year); + return true; +} +byte Arduino_colPins[COLS] = {2, 3, 18}; +byte Arduino_rowPins[ROWS] = {31, 33, 35, 37}; +Keypad kpd = Keypad( makeKeymap(keys), Arduino_rowPins, Arduino_colPins, ROWS, COLS ); + +const int rs = 12, en = 11, data4 = 36, data5 = 34, data6 = 32, data7 = 30; +LiquidCrystal lcd(rs, en, data4, data5, data6, data7); + + +void setup() { + if (!bme.begin()) { + Serial.println("Could not find a valid BME680 sensor, check wiring!"); + while (1); + bme.setTemperatureOversampling(BME680_OS_8X); + bme.setHumidityOversampling(BME680_OS_2X); + bme.setPressureOversampling(BME680_OS_4X); + bme.setIIRFilterSize(BME680_FILTER_SIZE_3); + bme.setGasHeater(320, 150); // 320*C for 150 ms + + } + //HW 2 + bool parse=false; + bool config=false; + + // get the date and time the compiler was run + if (getDate(__DATE__) && getTime(__TIME__)) { + parse = true; + // and configure the RTC with this info + if (RTC.write(tm)) { + config = true; + } + } + Serial.begin(115200); + while (!Serial); + Serial.println(F("BME680 test")); + + if (!bme.begin()) { + Serial.println("Could not find a valid BME680 sensor, check wiring!"); + while (1); + } + + // Set up oversampling and filter initialization + bme.setTemperatureOversampling(BME680_OS_8X); + bme.setHumidityOversampling(BME680_OS_2X); + bme.setPressureOversampling(BME680_OS_4X); + bme.setIIRFilterSize(BME680_FILTER_SIZE_3); + bme.setGasHeater(320, 150); // 320*C for 150 ms + + lcd.begin(16, 2); + Serial.println("About to write to LCD."); + Serial.println("LCD lines are 16 characters long."); + while (!Serial) ; // wait for Arduino Serial Monitor + delay(200); + if (parse && config) { + Serial.print("DS1307 configured Time="); + Serial.print(__TIME__); + Serial.print(", Date="); + Serial.println(__DATE__); + } else if (parse) { + Serial.println("DS1307 Communication Error :-{"); + Serial.println("Please check your circuitry"); + } else { + Serial.print("Could not parse info from the compiler, Time=\""); + Serial.print(__TIME__); + Serial.print("\", Date=\""); + Serial.print(__DATE__); + Serial.println("\""); + } + //HW 2 DONE + + //camera begin + #if !defined(SOFTWARE_SPI) +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + if(chipSelect != 53) pinMode(53, OUTPUT); // SS on Mega +#else + if(chipSelect != 10) pinMode(10, OUTPUT); // SS on Uno, etc. +#endif +#endif + + + Serial.println("VC0706 Camera snapshot test"); + + // see if the card is present and can be initialized: + if (!SD_thingy.begin(chipSelect)) { + Serial.println("Card failed, or not present"); + // don't do anything more: + return; + } + + // Try to locate the camera + if (cam.begin()) { + Serial.println("Camera Found:"); + } else { + Serial.println("No camera found?"); + return; + } + // Print out the camera version information (optional) + char *reply = cam.getVersion(); + if (reply == 0) { + Serial.print("Failed to get version"); + } else { + Serial.println("-----------------"); + Serial.print(reply); + Serial.println("-----------------"); + } + //CAMERA END + + //GPS BEGIN + GPS.begin(9600); + + // turn on RMC (recommended minimum) and GGA (fix data, including altitude) + GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); + GPS.sendCommand(PMTK_SET_NMEA_UPDATE_5HZ); +} +void updateTime() { + Serial.println("Setting the RTC Date"); + bool parse=false; + bool config=false; + if (getDate(__DATE__) && getTime(__TIME__)) { + parse = true; + if (RTC.write(tm)) { + config = true; + } + while (!Serial); + delay(200); + if (parse && config) { + Serial.print("DS1307 configured Time="); + Serial.print(__TIME__); + Serial.print(", Date="); + Serial.println(__DATE__); + } else if (parse) { + Serial.println("DS1307 Communication Error :-{"); + Serial.println("Please check your circuitry"); + } else { + Serial.print("Could not parse info from the compiler, Time=\""); + Serial.print(__TIME__); + Serial.print("\", Date=\""); + Serial.print(__DATE__); + Serial.println("\""); + } + } +} +void writeCSV(String picname, String speed_knots, String direction) { + Serial.println("opening the csv file"); + while (!Serial) { } + Serial.print("Initializing SD card reading software... "); + if (!SD_thingy.begin(SD_CS_PIN)) { + Serial.println("SD initialization failed!"); + delay(100); + exit(0); + } + /* + Serial.println("initialization done."); + if(SD_thingy.exists(filename)) { + SD_thingy.remove(filename); + delay(100); + } + */ + myFiley = SD_thingy.open(filename, FILE_WRITE); + if (myFiley) { + Serial.print("Writing to "); Serial.print(filename); Serial.println("..."); + } else { + Serial.print("error opening "); Serial.println(filename); + delay(100); + exit(0); + } + myFiley.print(__TIME__);myFiley.print(",");myFiley.print(__DATE__);myFiley.print(",");myFiley.print(picname);myFiley.print(",");myFiley.print(speed_knots);myFiley.print(",");myFiley.println(direction); + Serial.println("Finished writing to file. Now close it, open it, read it."); + lcd.setCursor(0,1); + lcd.print("Closing"); + myFiley.close(); +} +String picturename = "IMAGE00.JPG"; +void getphoto() { + cam.setImageSize(VC0706_640x480); // biggest + //cam.setImageSize(VC0706_320x240); // medium + //cam.setImageSize(VC0706_160x120); // small + + // You can read the size back from the camera (optional, but maybe useful?) + uint8_t imgsize = cam.getImageSize(); + Serial.print("Image size: "); + if (imgsize == VC0706_640x480) Serial.println("640x480"); + if (imgsize == VC0706_320x240) Serial.println("320x240"); + if (imgsize == VC0706_160x120) Serial.println("160x120"); + + Serial.println("Snap in 3 secs..."); + lcd.setCursor(0,1); + lcd.print("Taking pic"); + delay(3000); + + if (! cam.takePicture()) + Serial.println("Failed to snap!"); + else + Serial.println("Picture taken!"); + lcd.setCursor(0,1); + lcd.print("Pic taken!"); + + // Create an image with the name IMAGExx.JPG + char picname[13]; + strcpy(picname, "IMAGE00.JPG"); + for (int i = 0; i < 100; i++) { + picname[5] = '0' + i/10; + picname[6] = '0' + i%10; + picturename[5] = '0' + i/10; + picturename[6] = '0' + i%10; + + // create if does not exist, do not open existing, write, sync after write + if (! SD_thingy.exists(picname)) { + break; + } + } + + // Open the file for writing + File imgFile = SD_thingy.open(picname, FILE_WRITE); + + // Get the size of the image (frame) taken + uint32_t jpglen = cam.frameLength(); + Serial.print("Storing "); + lcd.setCursor(0,1); + lcd.print("Storing"); + Serial.print(jpglen, DEC); + Serial.print(" byte image."); + + int32_t time = millis(); + pinMode(8, OUTPUT); + // Read all the data up to # bytes! + byte wCount = 0; // For counting # of writes + while (jpglen > 0) { + // read 32 bytes at a time; + uint8_t *buffer; + uint8_t bytesToRead = min((uint32_t)32, jpglen); // change 32 to 64 for a speedup but may not work with all setups! + buffer = cam.readPicture(bytesToRead); + imgFile.write(buffer, bytesToRead); + if(++wCount >= 64) { // Every 2K, give a little feedback so it doesn't appear locked up + Serial.print('.'); + wCount = 0; + } + //Serial.print("Read "); Serial.print(bytesToRead, DEC); Serial.println(" bytes"); + jpglen -= bytesToRead; + } + imgFile.close(); + + time = millis() - time; + Serial.println("done!"); + lcd.setCursor(0,1); + lcd.print("Done!"); + Serial.print(time); Serial.println(" ms elapsed"); +} + +int count = 0; +void loop() { + updateTime(); +if (! bme.performReading()) { + Serial.println("Failed to perform reading :("); + return; + } + + + +GPS_query(); +Serial.println(GPS_minutes * 60 + GPS_seconds); +Serial.print("TIME: ");Serial.println(__TIME__); + + +lcd.setCursor(0,0); +lcd.print("Entries: ");lcd.print(count); +getphoto(); +Serial.println(picturename); +float BME_temp = bme.temperature; +Serial.println(BME_temp); +float GPS_second = GPS_minutes * 60 + GPS_seconds; +writeCSV(picturename,(String)GPS_second,(String)BME_temp); + delay(1000); + +Serial.println("pic taken"); +count++; +delay(1000); + +} +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(); + Serial.println("printing raw"); + + + 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) + { +Serial.println(GPS_sentence); + keep_trying = false; + + Serial.println("GPS navigation data not yet available. Try again later."); + + + 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..."); + } + + + } + + // 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 + + + // 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..."); + } + + } + + // 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 + + + // 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). + + } + + } 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 0000000000000000000000000000000000000000..02fbb7cb1fa9e593ea3a5e63c12698c95977fee9 --- /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). + + } + + } diff --git a/Homework 3/Question 1 Proof/gps_+_rtc_proof.png b/Homework 3/Question 1 Proof/gps_+_rtc_proof.png new file mode 100644 index 0000000000000000000000000000000000000000..7ae76bc8667e9d409d2ffc8847001293829383da Binary files /dev/null and b/Homework 3/Question 1 Proof/gps_+_rtc_proof.png differ diff --git a/Homework 3/Question 1 Proof/pic+sd+bme_proof.png b/Homework 3/Question 1 Proof/pic+sd+bme_proof.png new file mode 100644 index 0000000000000000000000000000000000000000..faa3ef2d53f694d9ba9c67cc6b28d60650d0e54c Binary files /dev/null and b/Homework 3/Question 1 Proof/pic+sd+bme_proof.png differ diff --git a/Homework 3/Question 2/.gitkeep b/Homework 3/Question 2/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Homework 3/Question 2/IMAGE00.JPG b/Homework 3/Question 2/IMAGE00.JPG new file mode 100644 index 0000000000000000000000000000000000000000..3bed0c3478f2f9d1a7d23c5f0d117acb6cbc1090 Binary files /dev/null and b/Homework 3/Question 2/IMAGE00.JPG differ diff --git a/Homework 3/Question 2/data.csv b/Homework 3/Question 2/data.csv new file mode 100644 index 0000000000000000000000000000000000000000..3e58ee294e4c819663d27b9508c00fdbbc604dda --- /dev/null +++ b/Homework 3/Question 2/data.csv @@ -0,0 +1,7 @@ +12:06:03,Feb 9 2023,IMAGE00.JPG,1387,25.98,,,,,,,,,, +12:06:03,Feb 9 2023,IMAGE01.JPG,1809,25.53,,,,,,,,,Mean,Standard Deviation +12:06:03,Feb 9 2023,IMAGE02.JPG,1809,25.13,,,,,,,,,24.92428571,0.612275782 +12:06:03,Feb 9 2023,IMAGE03.JPG,1809,24.8,,,,,,,,,, +12:06:03,Feb 9 2023,IMAGE04.JPG,2041,24.54,,,,,,,,,, +12:06:03,Feb 9 2023,IMAGE05.JPG,2960,24.33,,,,,,,,,, +12:06:03,Feb 9 2023,IMAGE06.JPG,3066,24.16,,,,,,,,,, diff --git a/Homework 3/Question 2/tempvstime.png b/Homework 3/Question 2/tempvstime.png new file mode 100644 index 0000000000000000000000000000000000000000..ce4d2ba0565f9c893a9349671d7639fbddfdaed5 Binary files /dev/null and b/Homework 3/Question 2/tempvstime.png differ