#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
#include <WiFi.h>
#include <time.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

// Cấu hình chân GPIO cho HUB75
#define R1_PIN 25
#define G1_PIN 26
#define B1_PIN 27
#define R2_PIN 14
#define G2_PIN 12
#define B2_PIN 13
#define A_PIN 23
#define B_PIN 19
#define C_PIN 5
#define D_PIN 17
#define E_PIN 18
#define LAT_PIN 4
#define OE_PIN 15
#define CLK_PIN 16

// Cấu hình WiFi
const char* ssid = "TEN_WIFI";
const char* password = "MAT_KHAU_WIFI";

// Cấu hình NTP
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 25200; // GMT+7
const int daylightOffset_sec = 0;

// Thêm cấu hình API
const char* openWeatherApiKey = "YOUR_API_KEY"; // Thay bằng API key của bạn
const char* ipApiUrl = "http://ip-api.com/json/";
String weatherApiUrl = "http://api.openweathermap.org/data/2.5/weather";

// Biến lưu thông tin thời tiết
float temperature = 0.0;
int humidity = 0;
String weatherDescription = "";
String location = "";

// Khởi tạo đối tượng matrix
MatrixPanel_I2S_DMA *matrix = nullptr;

// Thêm vào phần đầu file, sau các include
// Cấu hình cho LED Matrix
#define PANEL_RES_X 64      // Độ phân giải ngang
#define PANEL_RES_Y 64      // Độ phân giải dọc
#define PANEL_CHAIN 1       // Số lượng panel nối tiếp

// Thêm cấu hình DMA
#define MATRIX_WIDTH PANEL_RES_X * PANEL_CHAIN
#define MATRIX_HEIGHT PANEL_RES_Y

// Thêm class tính lịch âm
class LunarCalendar {
private:
    const int lunar_month_days[12] = {29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30};
    const double solar_term_base[24] = {
        4.6295, 19.4599, 34.2904, 49.1208, 64.0923, 78.9238,
        93.7553, 108.5867, 123.4182, 138.2497, 153.0811, 167.9126,
        182.744, 197.5755, 212.407, 227.2384, 242.0699, 256.9014,
        271.7328, 286.5643, 301.3958, 316.2272, 331.0587, 345.8902
    };

public:
    void convertSolar2Lunar(uint16_t solarYear, uint8_t solarMonth, uint8_t solarDay,
                           uint8_t &lunarDay, uint8_t &lunarMonth, uint16_t &lunarYear) {
        // Tính số ngày từ đầu năm
        int dayOfYear = getDayOfYear(solarYear, solarMonth, solarDay);
        
        // Tính năm âm lịch
        lunarYear = solarYear;
        if (dayOfYear < getLunarNewYear(solarYear)) {
            lunarYear--;
        }
        
        // Tính tháng và ngày âm lịch
        int daysSinceLunarNewYear = dayOfYear - getLunarNewYear(lunarYear);
        if (daysSinceLunarNewYear < 0) {
            daysSinceLunarNewYear += 365;
        }
        
        // Tính tháng âm lịch
        lunarMonth = 1;
        int daysInMonth = lunar_month_days[0];
        while (daysSinceLunarNewYear >= daysInMonth) {
            daysSinceLunarNewYear -= daysInMonth;
            lunarMonth++;
            if (lunarMonth > 12) break;
            daysInMonth = lunar_month_days[lunarMonth - 1];
        }
        
        // Tính ngày âm lịch
        lunarDay = daysSinceLunarNewYear + 1;
    }

private:
    int getDayOfYear(int year, int month, int day) {
        const int monthDays[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
        int dayOfYear = monthDays[month - 1] + day;
        
        // Thêm 1 ngày nếu là năm nhuận và sau tháng 2
        if (month > 2 && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)) {
            dayOfYear++;
        }
        return dayOfYear;
    }

    int getLunarNewYear(int solarYear) {
        // Đây là một ước tính đơn giản, có thể không chính xác 100%
        return (solarYear % 4 == 0 && solarYear % 100 != 0) || solarYear % 400 == 0 ? 50 : 49;
    }
};

// Hàm lấy vị trí từ IP
void getLocationFromIP() {
  HTTPClient http;
  http.begin(ipApiUrl);
  
  Serial.println("Đang lấy thông tin vị trí...");
  int httpCode = http.GET();
  
  if (httpCode > 0) {
    String payload = http.getString();
    Serial.println("IP API Response: " + payload);
    
    StaticJsonDocument<1024> doc;
    DeserializationError error = deserializeJson(doc, payload);
    
    if (!error) {
      float lat = doc["lat"];
      float lon = doc["lon"];
      location = doc["city"].as<String>();
      
      // Lấy thông tin thời tiết sau khi có vị trí
      getWeatherData(lat, lon);
    }
  } else {
    Serial.println("Lỗi khi lấy vị trí: " + String(httpCode));
  }
  http.end();
}

// Hàm lấy thông tin thời tiết
void getWeatherData(float lat, float lon) {
  String url = weatherApiUrl + "?lat=" + String(lat, 6) + "&lon=" + String(lon, 6) + 
               "&appid=" + openWeatherApiKey + "&units=metric";
               
  HTTPClient http;
  http.begin(url);
  
  Serial.println("Đang lấy thông tin thời tiết...");
  int httpCode = http.GET();
  
  if (httpCode > 0) {
    String payload = http.getString();
    Serial.println("Weather API Response: " + payload);
    
    StaticJsonDocument<1024> doc;
    DeserializationError error = deserializeJson(doc, payload);
    
    if (!error) {
      temperature = doc["main"]["temp"];
      humidity = doc["main"]["humidity"];
      weatherDescription = doc["weather"][0]["description"].as<String>();
      
      Serial.println("Nhiệt độ: " + String(temperature) + "°C");
      Serial.println("Độ ẩm: " + String(humidity) + "%");
      Serial.println("Thời tiết: " + weatherDescription);
    }
  } else {
    Serial.println("Lỗi khi lấy thông tin thời tiết: " + String(httpCode));
  }
  http.end();
}

void setup() {
  Serial.begin(115200);
  Serial.println("Khởi động hệ thống...");

  // Cấu hình LED Matrix
  HUB75_I2S_CFG mxconfig(
    PANEL_RES_X,   // Độ rộng module
    PANEL_RES_Y,   // Độ cao module
    PANEL_CHAIN    // Số lượng module nối tiếp
  );
  
  // Cấu hình chân GPIO
  mxconfig.gpio.r1 = R1_PIN;
  mxconfig.gpio.g1 = G1_PIN;
  mxconfig.gpio.b1 = B1_PIN;
  mxconfig.gpio.r2 = R2_PIN;
  mxconfig.gpio.g2 = G2_PIN;
  mxconfig.gpio.b2 = B2_PIN;
  mxconfig.gpio.a = A_PIN;
  mxconfig.gpio.b = B_PIN;
  mxconfig.gpio.c = C_PIN;
  mxconfig.gpio.d = D_PIN;
  mxconfig.gpio.e = E_PIN;
  mxconfig.gpio.lat = LAT_PIN;
  mxconfig.gpio.oe = OE_PIN;
  mxconfig.gpio.clk = CLK_PIN;

  // Thêm cấu hình nâng cao
  mxconfig.clkphase = false;
  mxconfig.driver = HUB75_I2S_CFG::FM6126A;     // Hoặc thay bằng loại IC của bạn

  // Tối ưu bộ nhớ
  mxconfig.double_buff = true;                   // Sử dụng double buffering
  mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_20M;    // Tốc độ I2S

  matrix = new MatrixPanel_I2S_DMA(mxconfig);
  matrix->begin();
  matrix->setBrightness(128);    // Giá trị từ 0-255

  // Kết nối WiFi
  Serial.println("Đang kết nối WiFi...");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nĐã kết nối WiFi");
  Serial.println("Địa chỉ IP: " + WiFi.localIP().toString());

  // Cấu hình thời gian
  Serial.println("Đang cấu hình thời gian...");
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);

  // Lấy thông tin vị trí và thời tiết
  getLocationFromIP();

  if (matrix == nullptr) {
    Serial.println("Không thể khởi tạo LED Matrix! Halt!");
    while (1) {
        delay(100);
    }
  }

  if (!matrix->begin()) {
    Serial.println("Không thể khởi động LED Matrix! Halt!");
    while (1) {
        delay(100);
    }
  }

  Serial.println("LED Matrix khởi động thành công!");
}

void loop() {
  struct tm timeinfo;
  if(!getLocalTime(&timeinfo)){
    Serial.println("Không thể lấy thời gian");
    return;
  }

  // Xóa màn hình
  matrix->fillScreen(matrix->color444(0, 0, 0));

  // Hiển thị thời gian
  char timeStr[9];
  sprintf(timeStr, "%02d:%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
  matrix->setTextColor(matrix->color444(15, 15, 15));
  matrix->setCursor(2, 5);
  matrix->print(timeStr);

  // Hiển thị ngày dương lịch
  char dateStr[11];
  sprintf(dateStr, "%02d/%02d/%04d", timeinfo.tm_mday, timeinfo.tm_mon + 1, timeinfo.tm_year + 1900);
  matrix->setCursor(2, 20);
  matrix->setTextColor(matrix->color444(0, 15, 0));
  matrix->print(dateStr);

  // Tính và hiển thị ngày âm lịch
  LunarCalendar lunar;
  uint8_t lunarDay, lunarMonth;
  uint16_t lunarYear;
  lunar.convertSolar2Lunar(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
                          lunarDay, lunarMonth, lunarYear);
  
  char lunarStr[11];
  sprintf(lunarStr, "%02d/%02d AL", lunarDay, lunarMonth);
  matrix->setCursor(2, 35);
  matrix->setTextColor(matrix->color444(15, 0, 0));
  matrix->print(lunarStr);

  // Hiển thị IP
  matrix->setCursor(2, 50);
  matrix->setTextColor(matrix->color444(0, 0, 15));
  matrix->print(WiFi.localIP().toString());

  // Thêm phần hiển thị thời tiết
  char weatherStr[20];
  sprintf(weatherStr, "%.1f°C %d%%", temperature, humidity);
  matrix->setCursor(2, 45);
  matrix->setTextColor(matrix->color444(0, 15, 15));
  matrix->print(weatherStr);

  // Hiển thị vị trí
  matrix->setCursor(2, 55);
  matrix->setTextColor(matrix->color444(0, 0, 15));
  matrix->print(location);

  // Cập nhật thông tin thời tiết mỗi 5 phút
  static unsigned long lastWeatherUpdate = 0;
  if (millis() - lastWeatherUpdate > 300000) {
    getLocationFromIP();
    lastWeatherUpdate = millis();
  }

  delay(1000);
} 

Có trong danh mục:

Arduino,