#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);
}
Bài viết trước