/*
ESP32 Moon Phase Calculator with OLED Display-CircuitSchools
Hardware Required:
- ESP32 Development Board
- SSD1306 OLED Display (128x64, I2C)
Wiring (I2C):
- OLED SDA -> GPIO 21
- OLED SCL -> GPIO 22
- OLED VCC -> 3.3V
- OLED GND -> GND
Libraries Required:
- Adafruit SSD1306
- Adafruit GFX Library
- WiFi (built-in)
- time.h (built-in)
*/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <WiFi.h>
#include <time.h>
// OLED Display Settings
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// WiFi Credentials - CHANGE THESE!
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// NTP Server Settings
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 19800; // GMT+5:30 for India (change as needed)
const int daylightOffset_sec = 0;
// Moon phase names
const char* moonPhaseNames[] = {
"New Moon",
"Waxing Crescent",
"First Quarter",
"Waxing Gibbous",
"Full Moon",
"Waning Gibbous",
"Last Quarter",
"Waning Crescent"
};
void setup() {
Serial.begin(115200);
// Initialize OLED
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println(F("Moon Phase Calc"));
display.println(F("Connecting WiFi..."));
display.display();
// Connect to WiFi
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi connected");
display.clearDisplay();
display.setCursor(0, 0);
display.println(F("WiFi Connected!"));
display.display();
delay(1000);
// Initialize time
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
delay(2000);
} else {
Serial.println("\nWiFi connection failed");
display.clearDisplay();
display.setCursor(0, 0);
display.println(F("WiFi Failed!"));
display.println(F("Check credentials"));
display.display();
delay(3000);
}
}
void loop() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
display.clearDisplay();
display.setCursor(0, 0);
display.println(F("Failed to get time"));
display.display();
delay(5000);
return;
}
// Calculate moon phase
float phase = calculateMoonPhase(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday);
int phaseIndex = (int)((phase / 29.53) * 8) % 8;
// Display moon phase
displayMoonPhase(phase, phaseIndex, timeinfo);
// Update every minute
delay(60000);
}
float calculateMoonPhase(int year, int month, int day) {
// Julian date calculation
if (month < 3) {
year--;
month += 12;
}
int a = year / 100;
int b = a / 4;
int c = 2 - a + b;
float e = floor(365.25 * (year + 4716));
float f = floor(30.6001 * (month + 1));
float jd = c + day + e + f - 1524.5;
// Days since known new moon (Jan 6, 2000)
float daysSinceNew = jd - 2451549.5;
// Number of new moons since Jan 6, 2000
float newMoons = daysSinceNew / 29.53;
// Current phase (0-29.53 days)
float phase = (newMoons - (int)newMoons) * 29.53;
return phase;
}
void displayMoonPhase(float phase, int phaseIndex, struct tm timeinfo) {
display.clearDisplay();
// Display date and time
display.setTextSize(1);
display.setCursor(0, 0);
char dateStr[20];
sprintf(dateStr, "%02d/%02d/%04d", timeinfo.tm_mday, timeinfo.tm_mon + 1, timeinfo.tm_year + 1900);
display.println(dateStr);
char timeStr[10];
sprintf(timeStr, "%02d:%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
display.println(timeStr);
// Draw moon
int centerX = 32;
int centerY = 42;
int radius = 15;
// Draw moon circle
display.fillCircle(centerX, centerY, radius, SSD1306_WHITE);
// Calculate illumination percentage
float illumination = (1 - cos((phase / 29.53) * 2 * PI)) / 2;
// Draw shadow for moon phase
if (phase < 14.765) {
// Waxing - shadow on left
int shadowWidth = (int)(radius * 2 * (1 - illumination));
for (int i = -radius; i <= radius; i++) {
int h = sqrt(radius * radius - i * i);
for (int j = -h; j < -h + shadowWidth; j++) {
display.drawPixel(centerX + j, centerY + i, SSD1306_BLACK);
}
}
} else {
// Waning - shadow on right
int shadowWidth = (int)(radius * 2 * illumination);
for (int i = -radius; i <= radius; i++) {
int h = sqrt(radius * radius - i * i);
for (int j = h - shadowWidth; j <= h; j++) {
display.drawPixel(centerX + j, centerY + i, SSD1306_BLACK);
}
}
}
// Display phase name
display.setCursor(64, 25);
display.setTextSize(1);
display.println(moonPhaseNames[phaseIndex]);
// Display illumination percentage
display.setCursor(64, 38);
char illumStr[15];
sprintf(illumStr, "%.1f%% lit", illumination * 100);
display.println(illumStr);
// Display age
display.setCursor(64, 50);
char ageStr[15];
sprintf(ageStr, "Day %.1f", phase);
display.println(ageStr);
display.display();
}