Since childhood, at night we always looked up at the night sky and seen a Moon, and it’s changing face has amazed us sometimes its full and sometime it half. That celestial rhythm is determined with moon phases and guided humanity for centuries. Now, with a little modern magic from an ESP32 microcontroller, you can bring that wonder right to your desk. This guide will show you how to build your own personal moon phase tracker, complete with a bright OLED display that shows the Moon’s current phase in real-time. It’s the perfect hands-on project for any stargazer, tech tinkerer, or anyone who’s ever felt a pull to the cosmos.
Table of Contents
Understanding Moon Phases: The Science Behind the Project
The Lunar Cycle Explained
The Moon completes one orbit around Earth approximately every 29.53 days, known as a synodic month. During this cycle, we observe different portions of the Moon’s illuminated surface, creating the familiar phases:
- New Moon (0% illumination): The Moon is between Earth and Sun
- Waxing Crescent (1-49% illumination): Increasing visibility after New Moon
- First Quarter (50% illumination): Half the Moon is visible
- Waxing Gibbous (51-99% illumination): Growing toward Full Moon
- Full Moon (100% illumination): Earth is between Moon and Sun
- Waning Gibbous (99-51% illumination): Decreasing after Full Moon
- Last Quarter (50% illumination): The other half becomes visible
- Waning Crescent (49-1% illumination): Diminishing toward New Moon
Astronomical Calculations
Our ESP32 moon phase calculator uses precise astronomical formulas to determine the current lunar phase. The core calculation involves:
- Julian Date Calculation: Converting Gregorian calendar dates to Julian dates for astronomical computations
- Lunar Cycle Position: Calculating days since the last known New Moon (January 6, 2000)
- Phase Determination: Using the synodic month (29.530588 days) to determine current phase
Project Features:
- Real-time moon phase calculation based on current date
- Visual moon representation with accurate shading
- 8 moon phases: New Moon, Waxing Crescent, First Quarter, Waxing Gibbous, Full Moon, Waning Gibbous, Last Quarter, Waning Crescent
- Displays:
- Current date and time
- Moon phase name
- Illumination percentage
- Moon age (days into cycle)
- Graphical moon with proper shadow
Project Components and Hardware Setup
Required Components
- ESP32 Development Board: Powerful microcontroller with WiFi capability
- OLED Display (128×64): I2C interface, SSD1306 driver
- Breadboard and Jumper Wires: For prototyping
- Micro-USB Cable: For power and programming
Circuit Diagram

The connections are very simple just connect the I2c OLED display with SDA and SCL and power connections as shown in the above circuit diagram.
OLED Display → ESP32
SDA → GPIO 21
SCL → GPIO 22
VCC → 3.3V
GND → GND
Important Notes:
- Always use the 3.3V power source for the OLED display
- Ensure proper grounding between all components
- Use appropriate pull-up resistors if experiencing I2C communication issues
Illuminate the Night: Build Your ESP32 Moon Phase Calculator with PCBWay
Ever dreamed of tracking lunar cycles from your workspace? Our ESP32 Moon Phase Calculator project combines a powerful microcontroller with a crisp OLED display to show real-time moon phases—a beautiful fusion of astronomy and DIY electronics. But a brilliant prototype deserves a professional foundation, and that’s where PCBWay transforms your vision into reality.
PCBWay offers comprehensive manufacturing services that elevate your project from breadboard to polished gadget. Their PCB fabrication delivers high-quality custom boards, whether you need standard FR4, flexible circuits, or complex rigid-flex designs. For flawless assembly, their SMT and through-hole PCB assembly services handle everything from fine-pitch components to BGA packages, with component sourcing available.
Beyond circuits, PCBWay’s 3D printing (FDM, SLA, SLS, MJF) creates stunning enclosures and custom parts, while CNC machining (3-, 4-, and 5-axis) precision-crafts metal or plastic components. Need robust housings? Their sheet metal fabrication and injection molding services deliver production-grade parts.
PCBWay also provides essential tools like laser-cut SMT stencils and online design tools (Gerber viewer, impedance calculators) to streamline your workflow. With strict quality control and global shipping, they’re the one-stop partner for innovators.
Ready to build your lunar tracker? Trust PCBWay to bring your brightest ideas to life—from PCBs to full product realization only at PCBWAY.COM
Setup and Program Code
After connecting all the required components, we have to upload the program code to ESP32 microcontroller to make it work as ESP32 Moon Phase Calculator using Arduino IDE.
Install Required Libraries (via Arduino IDE Library Manager):
- Adafruit SSD1306
- Adafruit GFX Library
After installing the OLED display libraries copy the below code and upload it to ESP32.
Configure the Code:
- Change
YOUR_WIFI_SSIDto your WiFi name - Change
YOUR_WIFI_PASSWORDto your WiFi password - Adjust
gmtOffset_secfor your timezone (currently set for India GMT+5:30)
/*
* 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 = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_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);
delay(60000); // Update every minute
}
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();
}
How the Code Works – Step by Step
1. Setup Phase
- Connects to WiFi (so it can get accurate time)
- Syncs with internet time servers (NTP)
- Initializes the OLED display
2. The Main Loop
Every minute, the program:
- Gets the current date and time
- Calculates the moon’s phase using a mathematical formula
- Updates the display with new information
3. Moon Phase Calculation
The calculateMoonPhase() function uses Julian Date mathematics to determine where the moon is in its 29.53-day cycle. It calculates how many days have passed since a known new moon (January 6, 2000) and figures out the current position in the lunar month.
4. Visual Display
The displayMoonPhase() function creates a visual representation by:
- Drawing a white circle for the moon
- Adding black “shadow” pixels to show the illuminated portion
- Displaying the phase name (like “Waxing Crescent”)
- Showing illumination percentage and moon “age” in days
Key Variables
phase: Current day in the 29.53-day lunar cycle (0 = New Moon, 14.765 = Full Moon)phaseIndex: Number from 0-7 that picks the correct phase nameillumination: Percentage of the moon that’s lit (0% to 100%)
What You’ll See on Screen
- Current date and time
- Animated moon graphic showing light/shadow
- Phase name (e.g., “First Quarter”)
- Illumination percentage
- Days since last new moon
Practical Applications
This moon phase calculator has numerous real-world applications:
- Astrophotography: Plan photography sessions around moon phases
- Gardening: Follow lunar gardening practices
- Fishing: Track optimal fishing times based on moon phases
- Astronomy Education: Teach lunar cycles in classrooms
- Cultural Events: Track holidays based on lunar calendars
Conclusion
Building an ESP32 Moon Phase Calculator is an excellent project that combines astronomy, programming, and electronics. This device provides accurate lunar information with a visually appealing display, making it both educational and practical. The project demonstrates the power of the ESP32 platform for creating Internet-connected devices that interact with real-world phenomena.
By following this guide, you’ve created a functional moon tracker that can be expanded with additional features like weather integration, voice alerts, or remote monitoring through a web interface. The possibilities for enhancement are limited only by your imagination!
Please remember to share your project variations and improvements in the below comments, and happy moon watching!
