/* * Updates: * 5/24/2019 shp leading space & "0" in date display * 5/25/2019 shp Correct display of 1:xx:xx PM & removed DEBUG printing */ /* Interface to GPS Unit * Rui Santos * Complete Project Details http://randomnerdtutorials.com */ /* Convert decimal Lat & Long to Maidenhead Grid Square * Steve WA3RTC 5/2019 * Latitude & Longitude in decimal degrees: * function is MH_CALC(Longitude, Latitude) * Maidenhead value returned to strMHead */ /* Determine and convert time to: * UTC if bUTC = true * LOC if bUTC = false * 24 hour format if LOC && bAMP = false * 12 hour format if LOC && bAMP = true */ /* Use RTC for display of time with correction to the * GPS supplied time. Allows for time display with * loss of signal from satellites */ //-------------------Add Libraries: /* Library: Wire.h * Two Wire Interface (TWI/I2C) for sending and receiving data * over a net of devices or sensors. Used for I2C LCD display and RTC. */ #include /* Library: LiquidCrystal_I2C.h * controlling liquid crystal displays (LCDs) with I2C interface. */ #include /* Library: TinyGPS++.h * Interface with NEO-6M. NEO-6M GPS module */ #include /* Library: SoftwareSerial.h * Software serial port to read data from GPS module */ #include /* Library: RTClib.h * Real Time Clock used to display UTC time * a fork of JeeLab's ... real time clock library for Arduino */ #include "RTClib.h" //-------------------Create Instances Objects //(lcd) Set the LCD address to 0x27 for a 20 chars and 4 line display LiquidCrystal_I2C lcd(0x27, 20, 4); //(gps) Create instance of TinyGPS++ object TinyGPSPlus gps; //(ss) Set constants for Software Serial ports and baud rate to read GPS static const int RXPin = 6, TXPin = 7; // static const int RXPin = 4, TXPin = 3; static const uint32_t GPSBaud = 9600; // The serial connection to the GPS device SoftwareSerial ss(RXPin, TXPin); //(rtc) Create instance of Real Time Clock as 'rtc' RTC_DS1307 rtc; //-------------------Define Global Variables //Set global values for Maidenhead Calculation Function float fltLatitude; //Input of Latitude from GPS float fltLongitude; //Input of Longitude from GPS String strLon; //Longitude in string formatted form for display String strLat; //Latitude in string formatted form for display String strMHead; //Resulting Maidenhead Grid Square (8 digit format) // UTC,LOC time change variables int const bUTCsw = 10; //Input 0 = UTC 1 = LOC int const bAPMsw = 11; //Input 0 = 12 hour mode if LOC time int const bDSTsw = 12; //Input 0 = Daylight Savings if LOC time bool bUTC; //true = UTC, false = LOC bool bAPM; //true = 12 hour, false = 24 hour format of LOC bool bDST; //true = Daylight Savings of LOC bool bTimeToCor = true; //if true, update RTC with next valid GPS time unsigned long ulTimeToCorr; //Track Time for periodic rtc update unsigned long ulTimeCk = 600000; //Update time in ms (10min = 600000) int iDiff = 5; //Hour offset from UTC for LOC (5 for Eastern Zone) int iMonthRol[] = {31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30}; int iDay; int iMonth; int iYear; int iHr; int iMin; int iSec; String sTime12; //Display value for "AM" or "PM" or " " AND "LOC" or "UTC" String sActive; //Display value for "A" Location read or "X" Location old or not read unsigned long ulLastGood; //Time since last good location read //----------------------------void setup() void setup(){ //begin Software Serial port to GPS unit ss.begin(GPSBaud); //begin Wire routine (I2C) for LCD and rtc Wire.begin(); //begin, clear and splash screen for the LCD sActive = "X"; //Initialize for old location data lcd.begin(); lcd.clear(); lcd.setCursor(0,3); lcd.print(sActive); //set inputs for clock mode switches pinMode(bUTCsw, INPUT_PULLUP); // 10 pinMode(bAPMsw, INPUT_PULLUP); // 11 pinMode(bDSTsw, INPUT_PULLUP); // 12 }//END void setup() //----------------------------void loop() void loop(){ //Read clock mode switches bUTC = true; bAPM = true; bDST = true; if(digitalRead(bUTCsw) == HIGH){bUTC = false;} if(digitalRead(bAPMsw) == HIGH){bAPM = false;} if(digitalRead(bDSTsw) == HIGH){bDST = false;} //This sketch displays information every time a new sentence is correctly encoded. //Check if last read was over 5 seconds old (or if mills() has rolled over - 50 days since reset) if((millis() < ulLastGood) || (millis() > (ulLastGood + 5000))){sActive = "X";} while (ss.available() > 0){ gps.encode(ss.read()); //Check for update and calculate Grid Square if true if (gps.location.isUpdated()){ fltLongitude = gps.location.lng(); fltLatitude = gps.location.lat(); ulLastGood = millis(); //Last good read is now sActive = "A"; //Display "A" for good read //call function to return Grid Square strMHead = MHEAD(fltLongitude,fltLatitude); //Setup input values as strings with formatting for display (9 characters with 4 decimal places) strLon = String(fltLongitude,4); strLon = PADstr(strLon,9); strLat = String(fltLatitude,4); strLat = PADstr(strLat,9); // Display Lat, Long, Grid to 4x20 LCD lcd.setCursor(11,1); lcd.print("Longitude"); lcd.setCursor(0,1); lcd.print(strLon); lcd.setCursor(11,2); lcd.print("Latitude"); lcd.setCursor(0,2); lcd.print(strLat); lcd.setCursor(11,0); lcd.print(strMHead); lcd.setCursor(0,3); lcd.print(sActive); } //END if (gps.location.isUpdated()) } //END while (ss.available() > 0) //Get & Display Date-Time //Check if time to update RTC from GPS if ((gps.time.isValid()) && (bTimeToCor)){TIMECOR();} if (millis()(ulTimeToCorr + ulTimeCk)){bTimeToCor = true;} //Display update of time if seconds have changed if(iSec != rtc.now().second()){ //Move RTC data into Active time display iDay = rtc.now().day(); iMonth = rtc.now().month(); iYear = rtc.now().year(); iHr = rtc.now().hour(); iMin = rtc.now().minute(); iSec = rtc.now().second(); sTime12 = " UTC"; //Check if Leap Year if(((iYear % 4) == 0) && ((iYear % 100) != 0)){iMonthRol[2] = 29;} if(bUTC != true) { //Setup for Daylight Savings time if displaying LOC int iDstOfset = 0; //sTime12 = " LOC"; if(bDST){iDstOfset = -1;} iHr = iHr - iDiff - iDstOfset; if(iHr < 0){ iHr = iHr + 24; iDay = iDay - 1; } if(iDay < 1){ iMonth = iMonth - 1; iDay = iMonthRol[iMonth]; } if(iMonth < 1){ iMonth = 12; iYear = iYear - 1; } if(bAPM) //Convert LOC time to 12 hr format { sTime12 = "AM LOC"; if (iHr >= 12){sTime12 = "PM LOC";} if(iHr > 12){iHr = iHr - 12;} } else { sTime12 = " LOC"; } } //END if(bUTC != true) DISPLAY_TIME(); } //END if(iSec != rtc.now().second()) } //END Loop() //----------------------------String MHEAD(float fltLon,float fltLat) String MHEAD(float fltLon,float fltLat){ //MHEAD function returns the grid square calculation to calling routine //Initialize variables used internal to function int intQ; String strMH; float InV1; float InV2; // Offset long/lat for initial calc InV1 = fltLon + 180; InV2 = fltLat + 90; // Set values for 1st MH digit - divide by 20, upper case alpha intQ = InV1 / 20.0; InV1 = ((InV1 / 20.0) - intQ) * 20.0; strMH = char(intQ + 65); // Set values for 2nd HM digit - divide by 10, upper case alpha intQ = InV2 / 10.0; InV2 = ((InV2 / 10.0) - intQ) * 10.0; strMH = strMH + char(intQ + 65); // Set values for 3rd MH digit - divide by 2, numeric intQ = InV1 / 2.0; InV1 = ((InV1 / 2.0) - intQ) * 2.0; strMH = strMH + String(intQ); // Set values for 4th HM digit - divide by 1, numeric intQ = InV2 / 1.0; InV2 = ((InV2 / 1.0) - intQ) * 1.0; strMH = strMH + String(intQ); // Set values for 5th MH digit - divide by 0.08333, lower case alpha intQ = InV1 / 0.08333; InV1 = ((InV1 / 0.08333) - intQ) * 0.08333; strMH = strMH + char(intQ + 97); // Set values for 6th HM digit - divide by 0.0416665, lower case alpha intQ = InV2 / 0.0416665; InV2 = ((InV2 / 0.0416665) - intQ) * 0.0416665; strMH = strMH + char(intQ + 97); // Set values for 7th MH digit - divide by 0.008333, numeric intQ = InV1 / 0.008333; InV1 = ((InV1 / 0.008333) - intQ) * 0.008333; strMH = strMH + String(intQ); // Set values for 8th HM digit - divide by 0.004166, numeric intQ = InV2 / 0.004166; InV2 = ((InV2 / 0.004166) - intQ) * 0.004166; strMH = strMH + String(intQ); return strMH; } //END MHEAD() function //----------------------------String PADstr(String strPV,int intLen) String PADstr(String strPV,int intLen){ //PADstr function adds leading " " for string value to align right sides in display if (strPV.length() > intLen){ return strPV;} else{ do { strPV = " " + strPV; } //END do while (strPV.length() < intLen); } //END else return strPV; } //END PADstr() function //----------------------------void TIMECOR() void TIMECOR(){ // Set CLock to GPS rtc.adjust(DateTime(gps.date.year(),gps.date.month(),gps.date.day(),gps.time.hour(),gps.time.minute(),gps.time.second())); bTimeToCor = false; //Set Time to correct flag as false ulTimeToCorr = millis(); //Renew correction timer } //END TIMECOR() //----------------------------void DISPLAY_TIME() void DISPLAY_TIME(){ lcd.setCursor(4,3); //Print Time if (iHr < 10) lcd.print("0"); lcd.print(iHr); lcd.print(":"); if (iMin < 10) lcd.print("0"); lcd.print(iMin); lcd.print(":"); if (iSec < 10) lcd.print("0"); lcd.print(iSec); lcd.print(" "); lcd.print(sTime12); lcd.setCursor(0,0); //Print Date if(iMonth < 10){lcd.print(" ");} lcd.print(iMonth); lcd.print("/"); if(iDay < 10){lcd.print("0");} lcd.print(iDay); lcd.print("/"); lcd.print(iYear); lcd.setCursor(0,3); lcd.print(sActive); } //END DISPLAY_TIME()