#include <Wire.h>
#include <RTClib.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
//
// ---- Display config ----
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1 // Reset pin not used with I2C
#define OLED_ADDR 0x3C // Change to 0x3D if your OLED uses that
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
//
// ---- RTC ----
RTC_DS3231 rtc;
//
// ---- Constants for astronomy approximations ----
// Reference new moon: 2000-01-06 18:14 UT (JD ≈ 2451550.1)
// Source: common approximation epoch for simple moon phase calc
const double NEW_MOON_JD_REF = 2451550.1;
const double SYNODIC_MONTH = 29.53058867;
//
// ---- Utility: convert date/time to (approx) Julian Day ----
double julianDay(int y, int m, int d, int hh, int mm, int ss) {
// Fliegel–Van Flandern algorithm (works for Gregorian dates)
if (m <= 2) { y -= 1; m += 12; }
int A = y / 100;
int B = 2 - A + (A / 4);
long C = (long)(365.25 * (y + 4716));
long D = (long)(30.6001 * (m + 1));
double dayFrac = (hh + (mm/60.0) + (ss/3600.0)) / 24.0;
return C + D + d + B - 1524.5 + dayFrac;
}
//
// ---- Moon phase: age (days) and illumination (approx) ----
void moonPhase(double jd, double &ageDays, int &illumPercent) {
double lunations = (jd - NEW_MOON_JD_REF) / SYNODIC_MONTH;
double frac = lunations - floor(lunations);
ageDays = frac * SYNODIC_MONTH; // 0..29.53
// Illumination approximation using a simple cosine model
// phase angle ≈ 2π * frac; illumination = (1 - cos(phase))/2
double phaseAngle = 2.0 * PI * frac;
double illum = 0.5 * (1.0 - cos(phaseAngle));
illumPercent = (int)round(illum * 100.0);
}
//
// ---- Tropical zodiac from date (rough, by date ranges) ----
// Uses common Western (tropical) zodiac date boundaries.
const char* zodiacFromDate(int m, int d) {
// date as MMDD integer for easy comparison
int md = m*100 + d;
if ((md >= 321 && md <= 419 )) return "Aries";
if ((md >= 420 && md <= 520 )) return "Taurus";
if ((md >= 521 && md <= 620 )) return "Gemini";
if ((md >= 621 && md <= 722 )) return "Cancer";
if ((md >= 723 && md <= 822 )) return "Leo";
if ((md >= 823 && md <= 922 )) return "Virgo";
if ((md >= 923 && md <= 1022)) return "Libra";
if ((md >= 1023 && md <= 1121)) return "Scorpio";
if ((md >= 1122 && md <= 1221)) return "Sagittarius";
// Capricorn wraps year end
if (md >= 1222 || md <= 119) return "Capricorn";
if ((md >= 120 && md <= 218 )) return "Aquarius";
return "Pisces"; // 2/19–3/20
}
//
// ---- Cycles: Metonic (19y), Callippic (76y), Olympiad (4y) ----
int metonicYear(int year) { return (year % 19) == 0 ? 19 : (year % 19); }
int callippicYear(int year) { int r = year % 76; return r == 0 ? 76 : r; }
int olympiadYear(int year) { int r = year % 4; return r == 0 ? 4 : r; }
//
// ---- Optional: set RTC once from compile time ----
// Set to true on first upload to set the RTC, then set back to false.
bool SET_RTC_FROM_COMPILE_TIME = false;
void setup() {
// I2C
Wire.begin(21, 22); // ESP32 default pins; adjust if needed
// Serial (debug)
Serial.begin(115200);
delay(200);
// Display init
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
// If it fails, try other address or wiring
for (;;){
Serial.println("SSD1306 init failed. Check wiring/address.");
delay(2000);
}
}
display.clearDisplay();
display.display();
// RTC init
if (!rtc.begin()) {
showFatal("RTC not found!");
}
if (rtc.lostPower()) {
Serial.println("RTC lost power, setting time to compile time.");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
} else if (SET_RTC_FROM_COMPILE_TIME) {
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
}
void loop() {
DateTime now = rtc.now();
// Build Julian Day
double jd = julianDay(now.year(), now.month(), now.day(),
now.hour(), now.minute(), now.second());
// Moon
double ageDays;
int illum;
moonPhase(jd, ageDays, illum);
// Zodiac & cycles
const char* zodiac = zodiacFromDate(now.month(), now.day());
int metonic = metonicYear(now.year());
int callippic = callippicYear(now.year());
int olympiad = olympiadYear(now.year());
// ---- Draw ----
display.clearDisplay();
// Header: time
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
char buf[32];
sprintf(buf, "%02d:%02d:%02d", now.hour(), now.minute(), now.second());
display.println(buf);
// Date line
sprintf(buf, "%04d-%02d-%02d", now.year(), now.month(), now.day());
display.println(buf);
// Moon line
// Age with 1 decimal
char moonBuf[32];
dtostrf(ageDays, 5, 1, moonBuf);
display.print("Moon: ");
display.print(moonBuf);
display.print(" d ");
display.print(illum);
display.println("%");
// Zodiac
display.print("Zodiac: ");
display.println(zodiac);
// Cycles
display.print("Metonic: ");
display.print(metonic);
display.print(" Callippic: ");
display.println(callippic);
display.print("Olympiad: ");
display.print(olympiad);
display.println(" of 4");
// Tiny “phase bar”
// Draw a simple horizontal bar to visualize illumination
int barX = 0, barY = 56, barW = 128, barH = 8;
display.drawRect(barX, barY, barW, barH, SSD1306_WHITE);
int fillW = map(illum, 0, 100, 0, barW - 2);
display.fillRect(barX + 1, barY + 1, fillW, barH - 2, SSD1306_WHITE);
display.display();
delay(250); // refresh ~4 fps
}
void showFatal(const char* msg) {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("FATAL:");
display.println(msg);
display.display();
while (true) { delay(1000); }
}
No comments:
Post a Comment