From 261390a3fb7be923895d08cfd8ae172b22895b54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?El=C3=ADas=20A=2E=20Angulo=20Klein?= Date: Sun, 21 Jul 2024 22:05:30 +0200 Subject: [PATCH 1/2] Added MoonPhase library --- src/MoonPhase.cpp | 96 +++++++++++++++++++++++++++++++++++++++++++++++ src/MoonPhase.h | 33 ++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 src/MoonPhase.cpp create mode 100644 src/MoonPhase.h diff --git a/src/MoonPhase.cpp b/src/MoonPhase.cpp new file mode 100644 index 0000000..7797c14 --- /dev/null +++ b/src/MoonPhase.cpp @@ -0,0 +1,96 @@ +// Calculate the phase and position of the moon for a given date. +// The algorithm is simple and adequate for many purposes. +// +// This software was originally adapted to javascript by Stephen R. Schmitt +// from a BASIC program from the 'Astronomical Computing' column of Sky & Telescope, +// April 1994, page 86, written by Bradley E. Schaefer. +// +// Subsequently adapted from Stephen R. Schmitt's javascript to c++ for the Arduino +// by Cyrus Rahman, this work is subject to Stephen Schmitt's copyright: +// +// Copyright 2004 Stephen R. Schmitt +// You may use or modify this source code in any way you find useful, provided +// that you agree that the author(s) have no warranty, obligations or liability. You +// must determine the suitability of this source code for your use. + +#include +#include "MoonPhase.h" + +// Names of lunar phases +static const char *phaseNames[] = {"New", "Evening Crescent", "First Quarter", + "Waxing Gibbous", "Full", "Waning Gibbous", + "Last Quarter", "Morning Crescent"}; +// Names of Zodiac constellations +static const char *zodiacNames[] = {"Pisces", "Aries", "Taurus", "Gemini", "Cancer", + "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", + "Capricorn", "Aquarius"}; +// Ecliptic angles of Zodiac constellations +static const float zodiacAngles[] = {33.18, 51.16, 93.44, 119.48, 135.30, 173.34, + 224.17, 242.57, 271.26, 302.49, 311.72, 348.58}; + +// Constructor initialization. +MoonPhase::MoonPhase() { + jDate = 0; + phase = 0; + age = 0; + fraction = 0; + distance = 0; + latitude = 0; + longitude = 0; + phaseName = zodiacName = ""; +} + +// Determine the Moon Phase and orbital positions for the specified time. +void +MoonPhase::calculate(time_t t) { + jDate = julianDate(t); + + // Calculate illumination (synodic) phase. + // From number of days since new moon on Julian date MOON_SYNODIC_OFFSET + // (1815UTC January 6, 2000), determine remainder of incomplete cycle. + phase = (jDate - MOON_SYNODIC_OFFSET) / MOON_SYNODIC_PERIOD; + phase -= floor(phase); + + // Calculate age and illumination fraction. + age = phase * MOON_SYNODIC_PERIOD; + fraction = (1.0 - cos(2 * M_PI * phase)) * 0.5; + phaseName = phaseNames[(int)(phase * 8 + 0.5) % 8]; + + // Calculate distance from anomalistic phase. + double distancePhase = (jDate - MOON_DISTANCE_OFFSET) / MOON_DISTANCE_PERIOD; + distancePhase -= floor(distancePhase); + distance = 60.4 - 3.3 * cos(2 * M_PI * distancePhase) + - 0.6 * cos(2 * 2 * M_PI * phase - 2 * M_PI * distancePhase) + - 0.5 * cos(2 * 2 * M_PI * phase); + + // Calculate ecliptic latitude from nodal (draconic) phase. + double latPhase = (jDate - MOON_LATITUDE_OFFSET) / MOON_LATITUDE_PERIOD; + latPhase -= floor(latPhase); + latitude = 5.1 * sin(2 * M_PI * latPhase); + + // Calculate ecliptic longitude from sidereal motion. + double longPhase = (jDate - MOON_LONGITUDE_OFFSET) / MOON_LONGITUDE_PERIOD; + longPhase -= floor(longPhase); + longitude = 360 * longPhase + + 6.3 * sin(2 * M_PI * distancePhase) + + 1.3 * sin(2 * 2 * M_PI * phase - 2 * M_PI * distancePhase) + + 0.7 * sin(2 * 2 * M_PI * phase); + if (longitude > 360) + longitude -= 360; + + // Select the Zodiac name. + zodiacName = zodiacNames[0]; + for (int i = 0; i < sizeof(zodiacAngles) / sizeof(float); i++) { + if (longitude < zodiacAngles[i]) { + zodiacName = zodiacNames[i]; + break; + } + } +} + +// Determine Julian date from Unix time. +// Provides marginally accurate results with older Arduino 4-byte double. +double +MoonPhase::julianDate(time_t t) { + return (t / 86400.0L + 2440587.5); +} diff --git a/src/MoonPhase.h b/src/MoonPhase.h new file mode 100644 index 0000000..b63d1f5 --- /dev/null +++ b/src/MoonPhase.h @@ -0,0 +1,33 @@ +#ifndef MoonPhase_h +#define MoonPhase_h + +#include + +#define MOON_SYNODIC_PERIOD 29.530588853 // Period of moon cycle in days. +#define MOON_SYNODIC_OFFSET 2451550.26 // Reference cycle offset in days. +#define MOON_DISTANCE_PERIOD 27.55454988 // Period of distance oscillation +#define MOON_DISTANCE_OFFSET 2451562.2 +#define MOON_LATITUDE_PERIOD 27.212220817 // Latitude oscillation +#define MOON_LATITUDE_OFFSET 2451565.2 +#define MOON_LONGITUDE_PERIOD 27.321582241 // Longitude oscillation +#define MOON_LONGITUDE_OFFSET 2451555.8 + +class MoonPhase { + public: + double jDate; + double phase; // 0 - 1, 0.5 = full + double age; // Age in days of current cycle + double fraction; // Fraction of illuminated disk + double distance; // Moon distance in earth radii + double latitude; // Moon ecliptic latitude + double longitude; // Moon ecliptic longitude + const char *phaseName; // New, Full, etc. + const char *zodiacName; // Constellation + + MoonPhase(); + void calculate(time_t); + + private: + double julianDate(time_t); +}; +#endif From 0422175d12ff6e1fb110ff2ee23a0e8d14a123dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?El=C3=ADas=20A=2E=20Angulo=20Klein?= Date: Mon, 22 Jul 2024 00:18:51 +0200 Subject: [PATCH 2/2] Added Moon Phase menu app. --- src/Watchy.cpp | 79 +++++++++++++++++++++++++++++++++++++++++++++----- src/Watchy.h | 2 ++ src/config.h | 4 +-- 3 files changed, 75 insertions(+), 10 deletions(-) diff --git a/src/Watchy.cpp b/src/Watchy.cpp index 172c246..1ab11f5 100644 --- a/src/Watchy.cpp +++ b/src/Watchy.cpp @@ -27,6 +27,7 @@ RTC_DATA_ATTR bool USB_PLUGGED_IN = false; RTC_DATA_ATTR tmElements_t bootTime; RTC_DATA_ATTR uint32_t lastIPAddress; RTC_DATA_ATTR char lastSSID[30]; +RTC_DATA_ATTR MoonPhase mp; void Watchy::init(String datetime) { esp_sleep_wakeup_cause_t wakeup_reason; @@ -39,7 +40,7 @@ void Watchy::init(String datetime) { RTC.init(); // Init the display since is almost sure we will use it display.epd2.initWatchy(); - + mp = MoonPhase(); switch (wakeup_reason) { #ifdef ARDUINO_ESP32S3_DEV case ESP_SLEEP_WAKEUP_TIMER: // RTC Alarm @@ -97,6 +98,9 @@ void Watchy::init(String datetime) { esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); break; } + + time_t epoch = makeTime(currentTime); + mp.calculate(epoch); deepSleep(); } void Watchy::deepSleep() { @@ -167,6 +171,8 @@ void Watchy::handleButtonPress() { case 6: showSyncNTP(); break; + case 7: + showMoonPhase(); default: break; } @@ -249,6 +255,8 @@ void Watchy::handleButtonPress() { case 6: showSyncNTP(); break; + case 7: + showMoonPhase(); default: break; } @@ -302,9 +310,9 @@ void Watchy::showMenu(byte menuIndex, bool partialRefresh) { const char *menuItems[] = { "About Watchy", "Vibrate Motor", "Show Accelerometer", "Set Time", "Setup WiFi", "Update Firmware", - "Sync NTP"}; + "Sync NTP", "Moon Phase"}; for (int i = 0; i < MENU_LENGTH; i++) { - yPos = MENU_HEIGHT + (MENU_HEIGHT * i); + yPos = MENU_HEIGHT/2 + (MENU_HEIGHT * i); display.setCursor(0, yPos); if (i == menuIndex) { display.getTextBounds(menuItems[i], 0, yPos, &x1, &y1, &w, &h); @@ -335,9 +343,9 @@ void Watchy::showFastMenu(byte menuIndex) { const char *menuItems[] = { "About Watchy", "Vibrate Motor", "Show Accelerometer", "Set Time", "Setup WiFi", "Update Firmware", - "Sync NTP"}; + "Sync NTP", "Moon Phase"}; for (int i = 0; i < MENU_LENGTH; i++) { - yPos = MENU_HEIGHT + (MENU_HEIGHT * i); + yPos = MENU_HEIGHT/2 + (MENU_HEIGHT * i); display.setCursor(0, yPos); if (i == menuIndex) { display.getTextBounds(menuItems[i], 0, yPos, &x1, &y1, &w, &h); @@ -404,6 +412,57 @@ void Watchy::showAbout() { guiState = APP_STATE; } +void Watchy::showMoonPhase() { + + RTC.read(currentTime); + time_t epoch = makeTime(currentTime); + mp.calculate(epoch); + + display.setFullWindow(); + display.fillScreen(GxEPD_BLACK); + display.setFont(&FreeMonoBold9pt7b); + display.setTextColor(GxEPD_WHITE); + display.setCursor(0, 10); + + display.setCursor(0, MENU_HEIGHT); + + display.print("Date: "); + display.println(mp.jDate); + + display.print("Phase: "); + display.println(mp.phase); + + display.print("Age: "); + display.print(mp.age); + display.println(" days"); + + display.print("Visibility: "); + display.print(mp.fraction); + display.println("%"); + + display.print("Distance: "); + display.print(mp.distance); + display.println(" er"); + + display.print("Latitude: "); + display.print(mp.latitude); + display.println("°"); + + display.print("Longitude: "); + display.print(mp.longitude); + display.println("°"); + + display.print("Ph.: "); + display.println(mp.phaseName); + + display.print("Zodiac: "); + display.println(mp.zodiacName); + + display.display(true); // full refresh + + guiState = APP_STATE; +} + void Watchy::showBuzz() { display.setFullWindow(); display.fillScreen(GxEPD_BLACK); @@ -443,7 +502,7 @@ void Watchy::setTime() { int8_t hour = currentTime.Hour; int8_t day = currentTime.Day; int8_t month = currentTime.Month; - int8_t year = currentTime.Year; //tmYearToY2k(currentTime.Year); + int8_t year = tmYearToY2k(currentTime.Year); #endif int8_t gmt = gmtOffset / 3600; @@ -580,7 +639,7 @@ void Watchy::setTime() { if (setIndex == SET_YEAR) { // blink minute digits display.setTextColor(blink ? GxEPD_WHITE : GxEPD_BLACK); } - display.print(year); + display.print(1970 + year); display.setTextColor(GxEPD_WHITE); display.print("/"); @@ -613,7 +672,7 @@ void Watchy::setTime() { #ifdef ARDUINO_ESP32S3_DEV tm.Year = year; #else - tm.Year = year; //y2kYearToTm(year); + tm.Year = y2kYearToTm(year); #endif tm.Hour = hour; tm.Minute = minute; @@ -1200,5 +1259,9 @@ bool Watchy::syncNTP(long gmt, String ntpServer) { tmElements_t tm; breakTime((time_t)timeClient.getEpochTime(), tm); RTC.set(tm); + //Update also moon calendar. + RTC.read(currentTime); + time_t epoch = makeTime(currentTime); + mp.calculate(epoch); return true; } diff --git a/src/Watchy.h b/src/Watchy.h index 4344eec..d264af8 100644 --- a/src/Watchy.h +++ b/src/Watchy.h @@ -17,6 +17,7 @@ #include "config.h" #include "esp_chip_info.h" #include "TimezonesGMT.h" +#include "MoonPhase.h" #ifdef ARDUINO_ESP32S3_DEV #include "Watchy32KRTC.h" #include "soc/rtc.h" @@ -86,6 +87,7 @@ public: void showMenu(byte menuIndex, bool partialRefresh); void showFastMenu(byte menuIndex); void showAbout(); + void showMoonPhase(); void showBuzz(); void showAccelerometer(); void showUpdateFW(); diff --git a/src/config.h b/src/config.h index e69968e..0133b46 100644 --- a/src/config.h +++ b/src/config.h @@ -99,8 +99,8 @@ #define MAIN_MENU_STATE 0 #define APP_STATE 1 #define FW_UPDATE_STATE 2 -#define MENU_HEIGHT 25 -#define MENU_LENGTH 7 +#define MENU_HEIGHT 24 +#define MENU_LENGTH 8 // set time #define SET_HOUR 0 #define SET_MINUTE 1