Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <Wire.h>
- #include <SPI.h>
- #include <WiFiS3.h>
- #include <U8g2lib.h>
- #include <RTClib.h>
- // Enter your 2.4 GHz Wi-Fi details here.
- const char WIFI_SSID[] = "YOUR_WIFI_NAME";
- const char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD";
- // SSD1315 128x64 OLED using its SSD1306-compatible command set.
- // Four-wire hardware SPI connections on the Arduino UNO R4 WiFi:
- // OLED SCK -> D13, OLED SDA/DIN -> D11, CS -> D10, DC -> D9, RES -> D4.
- // D12/CIPO/MISO is not used because the OLED only receives data.
- constexpr uint8_t OLED_CS_PIN = 10;
- constexpr uint8_t OLED_DC_PIN = 9;
- constexpr uint8_t OLED_RESET_PIN = 4;
- U8G2_SSD1306_128X64_NONAME_F_4W_HW_SPI u8g2(
- U8G2_R0, OLED_CS_PIN, OLED_DC_PIN, OLED_RESET_PIN);
- RTC_DS3231 rtc;
- bool rtcReady = false;
- // Six separately controlled LEDs, ordered from the moon's left edge to right.
- // Each bare LED needs its own resistor (470 ohms is a safe starting value).
- constexpr uint8_t MOON_LED_COUNT = 6;
- const uint8_t MOON_LED_PINS[MOON_LED_COUNT] = {2, 3, 5, 6, 7, 8};
- constexpr bool LED_ACTIVE_HIGH = true; // false for common-positive wiring
- // Bit 0 is the leftmost LED; bit 5 is the rightmost LED.
- // Order: full, waning gibbous, last quarter, waning crescent,
- // new, waxing crescent, first quarter, waxing gibbous.
- const uint8_t PHASE_LED_MASKS[8] = {
- 0b111111, 0b011111, 0b000111, 0b000001,
- 0b000000, 0b100000, 0b111000, 0b111110
- };
- constexpr uint8_t MOON_WIDTH = 30;
- constexpr uint8_t MOON_HEIGHT = 30;
- constexpr uint8_t MOON_X = (128 - MOON_WIDTH) / 2;
- constexpr uint8_t MOON_Y = 18;
- static const uint8_t PROGMEM full_moon_bits[] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0xff, 0x3f, 0x00,
- 0x80, 0xff, 0x7f, 0x00, 0xe0, 0xff, 0xff, 0x01, 0xf0, 0xff, 0xff, 0x03,
- 0xf0, 0xff, 0xff, 0x03, 0xf8, 0xff, 0xff, 0x07, 0xfc, 0xff, 0xff, 0x0f,
- 0xfc, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0xff, 0x0f, 0xfe, 0xff, 0xff, 0x1f,
- 0xfe, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xff, 0x1f,
- 0xfe, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xff, 0x1f,
- 0xfe, 0xff, 0xff, 0x1f, 0xfc, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0xff, 0x0f,
- 0xfc, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x07, 0xf0, 0xff, 0xff, 0x03,
- 0xf0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x01, 0x80, 0xff, 0x7f, 0x00,
- 0x00, 0xff, 0x3f, 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 };
- static const uint8_t PROGMEM waning_gibbous_bits[] = {
- 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x80, 0xff, 0xff, 0x00,
- 0xe0, 0xff, 0xff, 0x00, 0xf0, 0xff, 0x7f, 0x00, 0xf8, 0xff, 0x7f, 0x00,
- 0xf8, 0xff, 0x3f, 0x00, 0xfc, 0xff, 0x1f, 0x00, 0xfe, 0xff, 0x1f, 0x00,
- 0xfe, 0xff, 0x0f, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0xff, 0xff, 0x0f, 0x00,
- 0xff, 0xff, 0x0f, 0x00, 0xff, 0xff, 0x07, 0x00, 0xff, 0xff, 0x07, 0x00,
- 0xff, 0xff, 0x07, 0x00, 0xff, 0xff, 0x07, 0x00, 0xff, 0xff, 0x07, 0x00,
- 0xff, 0xff, 0x07, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0xfe, 0xff, 0x0f, 0x00,
- 0xfe, 0xff, 0x0f, 0x00, 0xfc, 0xff, 0x1f, 0x00, 0xf8, 0xff, 0x1f, 0x00,
- 0xf8, 0xff, 0x3f, 0x00, 0xf0, 0xff, 0x3f, 0x00, 0xe0, 0xff, 0xff, 0x00,
- 0x80, 0xff, 0xff, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x00, 0xf8, 0x1f, 0x00 };
- static const uint8_t PROGMEM last_quarter_bits[] = {
- 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xff, 0x07, 0x00, 0x80, 0xff, 0x01, 0x00,
- 0xe0, 0xff, 0x01, 0x00, 0xf0, 0xff, 0x00, 0x00, 0xf8, 0x7f, 0x00, 0x00,
- 0xf8, 0x3f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00,
- 0xfe, 0x1f, 0x00, 0x00, 0xfe, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00,
- 0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00,
- 0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00,
- 0xff, 0x1f, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00,
- 0xfe, 0x1f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00,
- 0xf8, 0x7f, 0x00, 0x00, 0xf0, 0xff, 0x00, 0x00, 0xe0, 0xff, 0x01, 0x00,
- 0x80, 0xff, 0x01, 0x00, 0x00, 0xff, 0x07, 0x00, 0x00, 0xf8, 0x0f, 0x00 };
- static const uint8_t PROGMEM crescent_old_bits[] = {
- 0x00, 0xf8, 0x07, 0x00, 0x00, 0xff, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00,
- 0xe0, 0x0f, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00,
- 0xf8, 0x01, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00,
- 0x7e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
- 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
- 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
- 0x3f, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00,
- 0x7e, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00,
- 0xf8, 0x01, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00,
- 0x80, 0x1f, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xf8, 0x07, 0x00 };
- static const uint8_t PROGMEM new_moon_bits[] = {
- 0x00, 0xf8, 0x07, 0x00, 0x00, 0x07, 0x38, 0x00, 0x80, 0x00, 0x40, 0x00,
- 0x60, 0x00, 0x80, 0x01, 0x10, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x04,
- 0x08, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x10,
- 0x02, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x20,
- 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20,
- 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20,
- 0x01, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x10,
- 0x02, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04,
- 0x08, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x02, 0x60, 0x00, 0x80, 0x01,
- 0x80, 0x00, 0x40, 0x00, 0x00, 0x07, 0x38, 0x00, 0x00, 0xf8, 0x07, 0x00 };
- static const uint8_t PROGMEM crescent_new_bits[] = {
- 0x00, 0xf8, 0x07, 0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x7e, 0x00,
- 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0xe0, 0x07,
- 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x80, 0x1f,
- 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x00, 0x3f,
- 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f,
- 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f,
- 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x80, 0x1f,
- 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0xe0, 0x07,
- 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0xfc, 0x01,
- 0x00, 0x00, 0x7e, 0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0xf8, 0x07, 0x00 };
- static const uint8_t PROGMEM first_quarter_bits[] = {
- 0x00, 0xfc, 0x07, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xe0, 0x7f, 0x00,
- 0x00, 0xe0, 0xff, 0x01, 0x00, 0xc0, 0xff, 0x03, 0x00, 0x80, 0xff, 0x07,
- 0x00, 0x00, 0xff, 0x07, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xfe, 0x1f,
- 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0xfc, 0x1f, 0x00, 0x00, 0xfc, 0x3f,
- 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfc, 0x3f,
- 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfc, 0x3f,
- 0x00, 0x00, 0xfe, 0x3f, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0xfe, 0x1f,
- 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xff, 0x07,
- 0x00, 0x80, 0xff, 0x07, 0x00, 0xc0, 0xff, 0x03, 0x00, 0xe0, 0xff, 0x01,
- 0x00, 0xe0, 0x7f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xfc, 0x07, 0x00 };
- static const uint8_t PROGMEM waxing_gibbous_bits[] = {
- 0x00, 0xfc, 0x07, 0x00, 0x00, 0xff, 0x3f, 0x00, 0xc0, 0xff, 0x7f, 0x00,
- 0xc0, 0xff, 0xff, 0x01, 0x80, 0xff, 0xff, 0x03, 0x80, 0xff, 0xff, 0x07,
- 0x00, 0xff, 0xff, 0x07, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0xfe, 0xff, 0x1f,
- 0x00, 0xfc, 0xff, 0x1f, 0x00, 0xfc, 0xff, 0x1f, 0x00, 0xfc, 0xff, 0x3f,
- 0x00, 0xfc, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0x3f,
- 0x00, 0xf8, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0x3f,
- 0x00, 0xf8, 0xff, 0x3f, 0x00, 0xfc, 0xff, 0x1f, 0x00, 0xfc, 0xff, 0x1f,
- 0x00, 0xfc, 0xff, 0x1f, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0xfe, 0xff, 0x07,
- 0x00, 0xff, 0xff, 0x07, 0x00, 0xff, 0xff, 0x03, 0xc0, 0xff, 0xff, 0x01,
- 0xc0, 0xff, 0x7f, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x00, 0xfe, 0x07, 0x00 };
- using MoonBitmap = const uint8_t *;
- static const MoonBitmap moonBitmaps[8] = {
- full_moon_bits,
- waning_gibbous_bits,
- last_quarter_bits,
- crescent_old_bits,
- new_moon_bits,
- crescent_new_bits,
- first_quarter_bits,
- waxing_gibbous_bits
- };
- static const char *const phaseNames[8] = {
- "Full Moon",
- "Waning Gibbous",
- "Last Quarter",
- "Old Crescent",
- "New Moon",
- "New Crescent",
- "First Quarter",
- "Waxing Gibbous"
- };
- double julianDate(int year, int month, int day);
- uint8_t moonPhase(uint8_t &daysToFullMoon);
- void drawCentered(uint8_t baselineY, const char *text);
- void drawMoonScreen();
- void drawStatusScreen(const char *line1, const char *line2);
- void scanI2C();
- bool syncRtcFromWiFi();
- void setPhaseLeds(uint8_t phase);
- void setup() {
- Serial.begin(115200);
- Wire.begin();
- delay(250);
- for (uint8_t i = 0; i < MOON_LED_COUNT; ++i) {
- pinMode(MOON_LED_PINS[i], OUTPUT);
- digitalWrite(MOON_LED_PINS[i], LED_ACTIVE_HIGH ? LOW : HIGH);
- }
- Serial.println("Moon phase display starting");
- scanI2C();
- u8g2.begin();
- u8g2.clearBuffer();
- drawStatusScreen("OLED STARTED", "Checking RTC...");
- u8g2.sendBuffer();
- delay(1000);
- rtcReady = rtc.begin();
- if (!rtcReady) {
- Serial.println("ERROR: DS3231 not found at I2C address 0x68");
- } else {
- Serial.println("DS3231 detected");
- const bool rtcLostPower = rtc.lostPower();
- if (!syncRtcFromWiFi() && rtcLostPower) {
- Serial.println("NTP unavailable; using sketch build time");
- rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
- }
- }
- }
- void loop() {
- u8g2.clearBuffer();
- if (rtcReady) {
- drawMoonScreen();
- } else {
- setPhaseLeds(4); // New-moon mask: all phase LEDs off.
- drawStatusScreen("RTC NOT FOUND", "Expected: 0x68");
- }
- u8g2.sendBuffer();
- delay(1000);
- }
- void drawStatusScreen(const char *line1, const char *line2) {
- u8g2.setFont(u8g2_font_6x12_tf);
- drawCentered(26, line1);
- drawCentered(46, line2);
- }
- void scanI2C() {
- uint8_t found = 0;
- Serial.println("I2C scan (RTC only; OLED is SPI):");
- for (uint8_t address = 1; address < 127; ++address) {
- Wire.beginTransmission(address);
- if (Wire.endTransmission() == 0) {
- Serial.print(" found 0x");
- if (address < 16) {
- Serial.print('0');
- }
- Serial.println(address, HEX);
- ++found;
- }
- }
- if (found == 0) {
- Serial.println(" no I2C devices found");
- }
- }
- bool syncRtcFromWiFi() {
- if (strcmp(WIFI_SSID, "YOUR_WIFI_NAME") == 0) {
- Serial.println("Wi-Fi not configured; keeping DS3231 time");
- return false;
- }
- Serial.print("Connecting to Wi-Fi");
- WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
- const unsigned long connectStarted = millis();
- while (WiFi.status() != WL_CONNECTED && millis() - connectStarted < 20000UL) {
- Serial.print('.');
- delay(500);
- }
- Serial.println();
- if (WiFi.status() != WL_CONNECTED) {
- Serial.println("Wi-Fi connection timed out");
- return false;
- }
- Serial.println("Wi-Fi connected; requesting network time");
- unsigned long epoch = 0;
- const unsigned long timeStarted = millis();
- while (epoch < 1700000000UL && millis() - timeStarted < 15000UL) {
- epoch = WiFi.getTime();
- if (epoch < 1700000000UL) {
- delay(1000);
- }
- }
- WiFi.disconnect();
- if (epoch < 1700000000UL) {
- Serial.println("Network time failed; keeping DS3231 time");
- return false;
- }
- rtc.adjust(DateTime(epoch)); // Store UTC in the DS3231.
- Serial.print("DS3231 synchronized to Unix time ");
- Serial.println(epoch);
- return true;
- }
- void setPhaseLeds(uint8_t phase) {
- const uint8_t mask = PHASE_LED_MASKS[phase & 7];
- for (uint8_t i = 0; i < MOON_LED_COUNT; ++i) {
- const bool on = mask & (1U << i);
- digitalWrite(MOON_LED_PINS[i], on == LED_ACTIVE_HIGH ? HIGH : LOW);
- }
- }
- void drawMoonScreen() {
- uint8_t daysToFullMoon = 0;
- const uint8_t phase = moonPhase(daysToFullMoon);
- setPhaseLeds(phase);
- // Small fixed-width title font. Its exact size is not relied on:
- // drawCentered() measures the text before positioning it.
- u8g2.setFont(u8g2_font_5x7_tf);
- drawCentered(9, "Moon Phase Calculator");
- u8g2.drawHLine(0, 12, 128);
- // The original 30x30 XBM moon images work with U8g2.
- u8g2.drawXBMP(
- MOON_X, MOON_Y, MOON_WIDTH, MOON_HEIGHT, moonBitmaps[phase]);
- // Show the phase name centered along the bottom.
- u8g2.setFont(u8g2_font_6x12_tf);
- drawCentered(63, phaseNames[phase]);
- // Keep the original small days-to-next-full-moon value at the right.
- char daysText[4];
- snprintf(daysText, sizeof(daysText), "%u", daysToFullMoon);
- u8g2.setFont(u8g2_font_5x7_tf);
- u8g2.drawStr(114, 28, daysText);
- }
- void drawCentered(uint8_t baselineY, const char *text) {
- const uint8_t width = u8g2.getStrWidth(text);
- const int16_t x = (128 - width) / 2;
- u8g2.drawStr(x > 0 ? x : 0, baselineY, text);
- }
- uint8_t moonPhase(uint8_t &daysToFullMoon) {
- const DateTime now = rtc.now();
- double cycle = julianDate(now.year(), now.month(), now.day());
- cycle = int(cycle - 2244116.75); // Original 1 Jan 1972 reference.
- cycle /= 29.53;
- const long completedCycles = long(cycle);
- const double cycleFraction = cycle - completedCycles;
- const double elapsedDays = cycleFraction * 29.53;
- daysToFullMoon = uint8_t(29.53 - elapsedDays);
- // Round to the nearest of eight phase sectors and wrap 8 back to 0.
- return (uint8_t(cycleFraction * 8.0 + 0.5)) & 7;
- }
- double julianDate(int year, int month, int day) {
- const int yy = year - int((12 - month) / 10);
- int mm = month + 9;
- if (mm >= 12) {
- mm -= 12;
- }
- const double k1 = 365.25 * (yy + 4172);
- const double k2 = int((30.6001 * mm) + 0.5);
- const double k3 = int(((((yy / 100) + 4) * 0.75) - 38));
- return k1 + k2 + day + 59 - k3;
- }
Advertisement
Add Comment
Please, Sign In to add comment