#include <Wire.h>
#include <RTClib.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
String getMoonPhaseName(float age);
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
RTC_DS3231 rtc;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
const int LED_PIN = 9;
const int BUTTON_PIN = 2;
int brightness = 0;
bool simulate = true;
bool preview = false;
bool lastButtonState = HIGH;
unsigned long simStartTime = 0;
unsigned long simTime = 0;
unsigned long simInterval = 5000; // simulate moon phases every 5 seconds
// helper function to return the name of the moon phase based on its age
String getMoonPhaseName(float age) {
if (age < 1.845) {
return "New Moon";
} else if (age < 5.555) {
return "Waxing Crescent";
} else if (age < 9.265) {
return "First Quarter";
} else if (age < 12.985) {
return "Waxing Gibbous";
} else if (age < 16.695) {
return "Full Moon";
} else if (age < 20.405) {
return "Waning Gibbous";
} else if (age < 24.115) {
return "Last Quarter";
} else if (age < 27.825) {
return "Waning Crescent";
} else {
return "New Moon";
}
}
void setup() {
Serial.begin(9600);
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
if (rtc.lostPower()) {
Serial.println("RTC lost power, lets set the time!");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
}
void loop() {
DateTime now = rtc.now();
// check for button press to toggle preview mode
int buttonState = digitalRead(BUTTON_PIN);
if (buttonState != lastButtonState && buttonState == LOW) {
if (!preview) { // enter preview mode
simStartTime = millis();
}
preview = !preview;
Serial.print("Preview mode: ");
Serial.println(preview);
if (preview) {
simulate = false;
} else {
simulate = true;
}
}
lastButtonState = buttonState;
if (simulate) {
// calculate the brightness based on the moon phase
unsigned long midnight = now.unixtime() - (now.unixtime() % 86400UL); // midnight in seconds
int seconds = now.unixtime() - midnight;
int minutes= seconds / 60;
int hours = minutes / 60;
int days = (now.unixtime() / 86400UL) - 1; // days since Jan 1, 1970 (0-indexed)
float moonAge = fmod(days + 0.5, 29.53);
brightness = map(moonAge, 0, 29.53, 255, 0);
// update the LED brightness
analogWrite(LED_PIN, brightness);
// display the time, date, and moon phase on the OLED display
display.clearDisplay();
display.setCursor(0, 0);
display.print("Time: ");
display.print(now.year(), DEC);
display.print('/');
display.print(now.month(), DEC);
display.print('/');
display.print(now.day(), DEC);
display.print(' ');
display.print(hours, DEC);
display.print(':');
if (minutes < 10) {
display.print('0');
}
display.print(minutes, DEC);
display.setCursor(0, 10);
display.print("Moon Phase: ");
display.print(getMoonPhaseName(moonAge));
display.setCursor(0, 20);
display.print("Brightness: ");
display.print(brightness);
display.display();
// print the time, date, and moon phase to the serial monitor
Serial.print("Time: ");
Serial.print(now.year(), DEC);
Serial.print('/');
Serial.print(now.month(), DEC);
Serial.print('/');
Serial.print(now.day(), DEC);
Serial.print(' ');
Serial.print(hours, DEC);
Serial.print(':');
if (minutes < 10) {
Serial.print('0');
}
Serial.print(minutes, DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.print(" Moon phase: ");
Serial.print(moonAge);
Serial.print(" Brightness: ");
Serial.println(brightness);
} else if (preview) {
// calculate the moon phase based on the simulated time
simTime = millis() - simStartTime;
float moonAge = fmod(simTime / (simInterval / 29.53), 29.53);
brightness = map(moonAge, 0, 29.53, 255, 0);
// update the LED brightness
analogWrite(LED_PIN, brightness);
// display the simulated time and moon phase on the OLED display
display.clearDisplay();
display.setCursor(0, 0);
display.print("Preview Mode");
display.setCursor(0, 10);
display.print("Moon Phase: ");
display.print(getMoonPhaseName(moonAge));
display.setCursor(0, 20);
display.print("Sim Time: ");
display.print(simTime);
display.display();
// print the simulated time and moon phase to the serial monitor
Serial.print("Simulation time: ");
Serial.print(simTime);
Serial.print(" Moon phase: ");
Serial.print(moonAge);
Serial.print(" Brightness: ");
Serial.println(brightness);
}
// check for serial input to set the simulation interval
if (Serial.available() > 0) {
String input = Serial.readStringUntil('\n');
if (input.startsWith("setinterval ")) {
simInterval = input.substring(12).toInt();
Serial.print("Simulation interval set to: ");
Serial.println(simInterval);
}
}
}