#include "moon.h"
#include "MoonPhase.h"
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <math.h>
#include <Arduino_GFX_Library.h>
#include "colors.h"
#define TFT_MISO -1
#define TFT_MOSI 7 // SDA
#define TFT_SCK 6 // SCL
#define TFT_CS 10
#define TFT_DC 2
#define TFT_RST -1
#define TFT_BL 3
/*
#define TFT_SCK 18 // esp:5 -> chip1:SCL
#define TFT_MOSI 23 // esp:4-> chip1:SDA
#define TFT_MISO GFX_NOT_DEFINED
#define TFT_CS 15 // esp:5 -> chip1:CS
#define TFT_DC 2 // esp:1 -> chip1:DC
#define TFT_RST 4 // esp:0 -> chip1:RST
*/
// Magistrala SPI dla ESP32
Arduino_DataBus *bus = new Arduino_ESP32SPI(TFT_DC, TFT_CS, TFT_SCK, TFT_MOSI);
Arduino_GFX *gfx = new Arduino_GC9A01(bus, TFT_RST, 0, true, 240, 240);
#define tft (*gfx)
// Alias, żeby reszta kodu mogła dalej używać "tft.xxx()"
#define tft (*gfx)
// RESZTA KONFIGU
// CONFIG
const char* ssid = "Wokwi-GUEST";
const char* password = "";
#define updateHour 1
// Timezone configuration
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 1 * 3600;
const int daylightOffset_sec = 3600;
// LOCATION CONFIG
#define USE_MANUAL_LOCATION true
const char* MANUAL_CITY = "Caino";
const char* MANUAL_COUNTRY = "Italy";
const char* MANUAL_TIMEZONE = "Europe/Rome";
const float MANUAL_LAT = 45.613418;
const float MANUAL_LON = 10.312439;
// Moon display parameters
const int CX = 120;
const int CY = 120;
const int CR = 120;
// MoonPhase object
MoonPhase moon;
void showmoon(float phase);
const char* getPhaseName(float phase, float fraction);
void displayConnectingMessage();
void processMoonPhase();
// DODANO: Funkcja do sterowania podświetleniem
void setBacklight(bool state) {
digitalWrite(TFT_BL, state ? HIGH : LOW);
}
// DODANO: Funkcja do płynnej regulacji jasności (PWM)
void setBacklightBrightness(uint8_t brightness) {
analogWrite(TFT_BL, brightness);
}
// FUNKCJE CZASU
void printLocalTime() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time");
return;
}
char timeString[64];
strftime(timeString, sizeof(timeString), "%A, %B %d %Y %H:%M:%S", &timeinfo);
Serial.printf("🕒 Current Time: %s", timeString);
}
// SETUP
void setup() {
Serial.begin(115200);
// Inicjalizacja pinu podświetlenia
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
setBacklight(true); // Włącz podświetlenie na start
// Lub użyj PWM dla regulacji jasności:
// ledcSetup(0, 5000, 8); // kanał 0, 5kHz, 8-bit
// ledcAttachPin(TFT_BL, 0);
// ledcWrite(0, 128); // 50% jasności
// Inicjalizacja Arduino_GFX
tft.begin();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
displayConnectingMessage();
delay(3000);
Serial.println("\n🌙 ESP32 Moon Phase Tracker");
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\n✅ Connected!");
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
Serial.print("Waiting for time synchronization");
struct tm timeinfo;
int retry = 0;
while (!getLocalTime(&timeinfo) && retry < 10) {
delay(1000);
Serial.print(".");
retry++;
}
if (retry >= 10) {
Serial.println("\n❌ Time synchronization failed!");
} else {
Serial.println("\n✅ Time synchronized!");
printLocalTime();
}
}
// LOOP
void loop() {
if (WiFi.status() == WL_CONNECTED) {
if (USE_MANUAL_LOCATION) {
Serial.println("\n📍 Manual Location: Caino, Italy");
Serial.printf("🌐 Timezone: %s\n", MANUAL_TIMEZONE);
Serial.printf("📡 Coordinates: %.6f, %.6f\n", MANUAL_LAT, MANUAL_LON);
processMoonPhase();
} else {
HTTPClient http;
http.begin("http://ip-api.com/json/");
int httpCode = http.GET();
if (httpCode == 200) {
String payload = http.getString();
StaticJsonDocument<512> doc;
deserializeJson(doc, payload);
String city = doc["city"];
String country = doc["country"];
String timezone = doc["timezone"];
float lat = doc["lat"];
float lon = doc["lon"];
Serial.printf("\n📍 Detected Location: %s, %s\n", city.c_str(), country.c_str());
Serial.printf("🌐 Timezone: %s\n", timezone.c_str());
Serial.printf("📡 Coordinates: %.4f, %.4f\n", lat, lon);
processMoonPhase();
} else {
Serial.printf("❌ HTTP Error: %d\n", httpCode);
Serial.println("⚠️ Fallback to manual location");
processMoonPhase();
}
http.end();
}
} else {
Serial.println("❌ WiFi disconnected!");
WiFi.begin(ssid, password);
}
Serial.println("\n⏰ Next update in 1 hour...");
delay(updateHour * 60 * 60 * 1000);
}
// LICZENIE FAZY KSIĘŻYCA
void processMoonPhase() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("❌ Failed to obtain time");
return;
}
// Library calculation at current time
time_t t = time(NULL);
moon.calculate(t);
// Rysowanie
showmoon((float)moon.phase);
// Kierunek cyklu
bool waxing = (moon.phase < 0.5f); // true = przybywa
const char* arrow = waxing ? "⬆️" : "⬇️";
const char* dir = waxing ? "przybywa" : "ubywa";
// RAW DATA FROM LIBRARY
Serial.println("====== MOONPHASE (library) ======");
Serial.printf("🗓️ Data: %04d-%02d-%02d\n", timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday);
Serial.printf("🌙 Faza (0..1): %.6f %s %s\n", moon.phase, arrow, dir); // 0 - 1, 0.5 = full
Serial.printf("🌙 Nazwa fazy Księżyca: %s\n", moon.phaseName);
Serial.printf("🎂 Wiek w dniach bieżącego cyklu: %.4f\n", moon.age);
Serial.printf("🌓 Ułamek oświetlonego dysku (0..1): %.6f\n", moon.fraction);
Serial.printf("🌕 Oświetlenie: %.1f%%\n", moon.fraction * 100.0);
Serial.printf("🔍 Odległość Księżyca w promieniach Ziemi: %.3f\n", moon.distance); // Earth radii (as in lib)
Serial.printf("🔍 Szerokość ekliptyczna Księżyca: %.3f°\n", moon.latitude);
Serial.printf("🔍 Długość ekliptyczna Księżyca: %.3f°\n", moon.longitude);
Serial.printf("♋ Nazwa konstelacji zodiaku: %s\n", moon.zodiacName);
}
// RYSOWANIE KSIĘŻYCA
void showmoon(float /*unused*/) {
const float EDGE = 0.985f;
const float TILT_DEG = 0.0f;
const float TILT = TILT_DEG * (M_PI / 180.0f);
const int MIRROR = 1;
float k = (float)moon.fraction; // 0..1
bool waxing = (moon.phase < 0.5f); // ⬆️/⬇️
// Tło – bitmapa Księżyca
// Jeśli kolory będą poprzestawiane, spróbuj:
// tft.draw16bitBeRGBBitmap(0, 0, (uint16_t*)moonallArray[0], 240, 240);
tft.draw16bitRGBBitmap(0, 0, (uint16_t*)moonallArray[0], 240, 240);
float d = EDGE * (2.0f * CR * k);
float dx = d * cosf(TILT);
float dy = d * sinf(TILT);
int cx = (int)lroundf(CX + MIRROR * (waxing ? -dx : +dx));
int cy = (int)lroundf(CY + MIRROR * (waxing ? +dy : -dy));
cx = constrain(cx, -CR, 240 + CR);
cy = constrain(cy, -CR, 240 + CR);
const int R_FULL = 120;
const int R_RING = 118;
const float D_EPS = 1.5f;
int Rshadow = (d < D_EPS) ? R_FULL : R_RING;
// Cień
tft.fillCircle(cx, cy, Rshadow, TFT_BLACK);
bool brightRight = waxing ^ (MIRROR == -1);
Serial.printf("🌘 k=%.4f (%.1f%%) | phase=%.6f %s | d=%.1f | Shadow: X=%d Y=%d R=%d | Bright: %s\n", k, k * 100.0f, moon.phase, waxing ? "⬆️" : "⬇️", d, cx, cy, Rshadow, brightRight ? "D (right)" : "C (left)");
}
// FAZA – NAZWA (Z LIBKI)
const char* getPhaseName(float phase, float fraction) {
// Teraz zbędne – MoonPhase ma własne phaseName
(void)phase;
(void)fraction;
return moon.phaseName;
}
// EKRAN "CONNECTING..."
void displayConnectingMessage() {
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.setTextSize(1);
const char* msg = "Connecting";
int16_t x1, y1;
uint16_t w, h;
// Adafruit_GFX style: wyznaczamy szerokość tekstu i centrowanie
tft.getTextBounds(msg, 0, 0, &x1, &y1, &w, &h);
int16_t x = (240 - (int)w) / 2;
int16_t y = (240 + (int)h) / 2;
tft.setCursor(x, y);
tft.print(msg);
}