#include #include //for screen #include //for screen #include #include //for RTC #include //for RTC #include "SparkFun_Ublox_Arduino_Library_Series_6_7.h" //for gps #include //https://github.com/PaulStoffregen/Time //#include //for easy date calc https://inductive-kickback.com/projects/chronos/ #include //https://github.com/jpb10/SolarCalculator #include "I2C_eeprom.h" //for EEPROM https://github.com/RobTillaart/I2C_EEPROM #include "helperFunctions.h" #include "images.h" #include "pico/stdlib.h" #define GPS_POWER_PIN 2 #define INTERRUPT_PIN 3 unsigned long timer = 0UL; enum sunriseORSunset { SHOW_SUNRISE, SHOW_SUNSET }; enum sunriseORSunset showSunriseOrSunset = SHOW_SUNSET; //setup screen U8G2_SH1106_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R2, 17, 21, 20); uint8_t *buf; char outString[99]; //RTC setup DS3231 rtc; bool h12; bool pmt; bool setRtcDateAndTime = true; bool setDate = true; bool RTCsetTime = true; //GPS setup SFE_UBLOX_GPS myGPS; double lat = 0; double lng = 0; bool isGPSOn = true; bool doSunsetCalcs = true; bool locationDataSet = false; //sunset setup int tzOff = 13; //local TZ offset bool centuryBit; //needed to get month from RTC double az, elev; //for sun location in the sky double transit, sunrise, sunset; //sun related times double civilTransit, civilRiise, civilSet; //astro related times double nextDayTransit, nextDaySunrise, nextDaySunset; // vars to be used for the following day cals double nextDayCivilTransit, nextDayCivilRise, nextDayCivilSet; char str[6]; //needed to convert hours to string //setup EEPROM //I2C_eeprom store(0x50, I2C_DEVICESIZE_24LC256); struct locationData { double lat = 0; double lng = 0; } location; void gpsOff() { digitalWrite(GPS_POWER_PIN, LOW); isGPSOn = false; } void gpsOn() { digitalWrite(GPS_POWER_PIN, HIGH); isGPSOn = true; } void setLowCPUSpeed() { set_sys_clock_pll(900000000, 6, 6); //set cpu to 15 MHZ } void setHighCPUSpeed() { set_sys_clock_pll(1596000000, 6, 2); //set cpu to 133MHZ } /* void wakeGPS() { setHighCPUSpeed(); gpsOn(); connectGPS(); doSunsetCalcs = true; }*/ void connectGPS() { //need to do this everytime the GPS turns back on do { //set gps to 115200 baudrate code comes from sparkfun examples Serial.println("GNSS: trying 115200 baud"); Serial1.begin(115200); if (myGPS.begin(Serial1) == true) break; delay(100); Serial.println("GNSS: trying 9600 baud"); Serial1.begin(9600); if (myGPS.begin(Serial1) == true) { Serial.println("GNSS: connected at 9600 baud, switching to 115200"); myGPS.setSerialRate(115200); delay(100); } else { delay(2000); //Wait a bit before trying again to limit the Serial output } } while (1); myGPS.setUART1Output(COM_TYPE_UBX); //Set the UART1 port to output UBX only (turn off NMEA noise) myGPS.setNavigationFrequency(1); myGPS.setDynamicModel(DYN_MODEL_PORTABLE); } void setup() { //Screen setup u8g2.begin(); u8g2.clearDisplay(); u8g2.setPowerSave(0); u8g2.setFont(u8g2_font_ncenB14_tr); u8g2.clearBuffer(); sprintf(outString, "SUNCLOCK"); //show start up text u8g2.drawStr(7, 35, outString); u8g2.sendBuffer(); //interrupt pin // pinMode(INTERRUPT_PIN, INPUT_PULLUP); //attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), wakeGPS, FALLING); //RTC setup Wire.begin(); rtc.setClockMode(false); //GPS setup Serial1.setRX(1); Serial1.setTX(0); pinMode(GPS_POWER_PIN, OUTPUT_2MA); gpsOn(); connectGPS(); //RTC time set if (isGPSOn && myGPS.getPVT()) { //myGPS.getPVT() will return true if data is available rtc.setYear(myGPS.getYear()); rtc.setMonth(myGPS.getMonth()); rtc.setDate(myGPS.getDay()); rtc.setHour(myGPS.getHour()); rtc.setMinute(myGPS.getMinute()); rtc.setSecond(myGPS.getSecond()); } if (isGPSOn && myGPS.getPVT()) { //gps has to be on to set lat and long, once gps chords are set the gps is turned off so no need to set gps chords. lat = myGPS.getLatitude(); lng = myGPS.getLongitude(); lat = lat / 10000000; lng = lng / 10000000; locationDataSet = true; gpsOff(); } timer = millis(); //get time so we only set the clock once per second } void loop() { if (isGPSOn && myGPS.getPVT()) { //myGPS.getPVT() will return true if data is available rtc.setYear(myGPS.getYear()); rtc.setMonth(myGPS.getMonth()); rtc.setDate(myGPS.getDay()); rtc.setHour(myGPS.getHour()); rtc.setMinute(myGPS.getMinute()); rtc.setSecond(myGPS.getSecond()); } if (isGPSOn && myGPS.getPVT()) { //gps has to be on to set lat and long, once gps chords are set the gps is turned off so no need to set gps chords. lat = myGPS.getLatitude(); lng = myGPS.getLongitude(); lat = lat / 10000000; lng = lng / 10000000; locationDataSet = true; } if (isGPSOn && lat != 0 && lng != 0) { gpsOff(); } if (millis() > timer + 1000) { DateTime tm(rtc.getYear(), (int)rtc.getMonth(centuryBit), (int)rtc.getDate(), (int)rtc.getHour(h12, pmt), rtc.getMinute(), (int)rtc.getSecond()); //set date and time to use for the rest of the loop if (doSunsetCalcs) { //get sunset time calcSunriseSunset(tm.year(), tm.month(), tm.day(), lat, lng, transit, sunrise, sunset); //get civilSet time calcCivilDawnDusk(tm.year(), tm.month(), tm.day(), lat, lng, civilTransit, civilRiise, civilSet); //calcs for sunrise times uint16_t year = tm.year(); uint16_t month = tm.month(); uint16_t day = tm.day(); if (((tm.hour() + tzOff) % 24) > 6) { //only increment day if it is before midnight //detect when day is at the end of the month and increment month and set day to 1 otherwise increment day by 1 if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) { if (day == 31) { if (month == 12) { year++; month = 1; day = 1; } else { month++; day = 1; } } else { day++; } } else if (month == 4 || month == 6 || month == 9 || month == 11) { if (day == 30) { month++; day = 1; } else { day++; } } else { if (month == 2) { if (year % 400 == 0 || year % 4 == 0) { //special case for leap year if (day == 29) { month++; day = 1; } else { if (day == 28) { month++; day = 1; } } } else { day++; } } } } //calc sunrise times //calcSunriseSunset(tm.year(), tm.month(), tm.day(), lat, lng, nextDaySunrise, nextDaySunrise, nextDaySunset); calcSunriseSunset(year, month, day, lat, lng, nextDayTransit, nextDaySunrise, nextDaySunset); calcCivilDawnDusk(year, month, day, lat, lng, nextDayCivilTransit, nextDayCivilRise, nextDayCivilSet); } //get current time double dhour = tm.hour(); double dmin = tm.minute(); double dsec = tm.second(); //calc time to sunset time double currentTimeInHours = dhour + (dmin / 60) + (dsec / 60 / 60); double TimeToSunset = sunset - currentTimeInHours; double timeTocivilSet = civilSet - currentTimeInHours; //calc time to sunrise time double timeToSunrise = nextDaySunrise + tzOff - currentTimeInHours + tzOff; //+tzoff is a work around as library gives incorrect time double timeToCivilRise = nextDayCivilRise + tzOff - currentTimeInHours + tzOff; if (TimeToSunset < 0) { TimeToSunset = 0; } if (timeTocivilSet < 0) { timeTocivilSet = 0; showSunriseOrSunset = SHOW_SUNRISE; } if (timeToSunrise < 0) { timeToSunrise = 0; showSunriseOrSunset = SHOW_SUNSET; } if (timeToCivilRise < 0) { timeToCivilRise = 0; } u8g2.clearBuffer(); sprintf(outString, "%.2f:%.2f", lat, lng); //show location chords u8g2.drawStr(0, 35, outString); sprintf(outString, "%s", hoursToString(currentTimeInHours + tzOff, str)); //show current time u8g2.drawStr(0, 55, outString); if (showSunriseOrSunset == SHOW_SUNSET) { sprintf(outString, "%s", hoursToString(TimeToSunset, str)); //show time to sunset u8g2.drawStr(0, 15, outString); sprintf(outString, "%s", hoursToString(timeTocivilSet, str)); //show civilSet time u8g2.drawStr(75, 15, outString); } if (showSunriseOrSunset == SHOW_SUNRISE) { sprintf(outString, "%s", hoursToString(timeToSunrise, str)); //show time to sunrise // sprintf(outString, "%f", timeToSunrise); u8g2.drawStr(0, 15, outString); sprintf(outString, "%s", hoursToString(timeToCivilRise, str)); //show civilRise time u8g2.drawStr(75, 15, outString); } u8g2.sendBuffer(); if (locationDataSet == true) { //set to low power mode doSunsetCalcs = false; //stop doing sunset calcs once we have the gps data. locationDataSet = false; //this this to false because location data will need to be reset once GPS turns back on setLowCPUSpeed(); } timer = millis(); } if (BOOTSEL) { rp2040.rebootToBootloader(); } }