307 lines
9.6 KiB
C++
307 lines
9.6 KiB
C++
#include <Arduino.h>
|
|
#include <SPI.h> //for screen
|
|
#include <U8g2lib.h> //for screen
|
|
#include <stdlib.h>
|
|
#include <Wire.h> //for RTC
|
|
#include <DS3231.h> //for RTC
|
|
#include "SparkFun_Ublox_Arduino_Library_Series_6_7.h" //for gps
|
|
#include <TimeLib.h> //https://github.com/PaulStoffregen/Time
|
|
//#include <Chronos.h> //for easy date calc https://inductive-kickback.com/projects/chronos/
|
|
#include <SolarCalculator.h> //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();
|
|
}
|
|
}
|