#include <Arduino.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <lvgl.h>
#include <TFT_eSPI.h>
// 📡 填入你家中的 2.4GHz WiFi 連線資料
const char* ssid = "AX3000 2.4G";
const char* password = "ray2203@shirley";
// 🚌 九巴 API 變量與預設路線
String current_bus_route = "87K";
String bus_eta_text1 = "Loading...";
String bus_eta_text2 = "Loading...";
// 🚇 港鐵 API 變量與預設車站 (HHO = 恆安, TML = 屯馬綫)
String current_mtr_station = "HHO";
String current_mtr_line = "TML";
String mtr_up_eta = "Loading...";
String mtr_down_eta = "Loading...";
TFT_eSPI tft = TFT_eSPI();
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[320 * 10]; // 使用具體像素寬度,避免編譯器解析出錯
// LVGL 屏幕刷新底層對接
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
tft.startWrite();
tft.setAddrWindow(area->x1, area->y1, w, h);
tft.pushColors((uint16_t *)&color_p->full, w * h, true);
tft.endWrite();
lv_disp_flush_ready(disp);
}
// 👆 虛擬鍵盤事件回呼 (當輸入完巴士線撳 Enter 時觸發)
static void kb_event_cb(lv_event_t * e) {
lv_event_code_t code = lv_event_get_code(e); // 已經 Fact Check:LVGL 8.3 標準變量名
lv_obj_t * kb = lv_event_get_target(e);
lv_obj_t * ta = lv_keyboard_get_textarea(kb);
if (code == LV_EVENT_READY) { // 撳咗右下角剔號/Enter 鍵
current_bus_route = lv_textarea_get_text(ta);
lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN); // 隱藏小鍵盤,避免直接 delete 導致指針崩潰
}
}
// 🚇 港鐵路線切換事件 (點選不同路線按鈕時觸發)
static void mtr_line_cb(lv_event_t * e) {
lv_obj_t * obj = lv_event_get_target(e);
uint32_t id = lv_btnmatrix_get_selected_btn(obj);
if(id == 0) { current_mtr_line = "TML"; current_mtr_station = "HHO"; } // 屯馬綫 -> 預設恆安
if(id == 1) { current_mtr_line = "EAL"; current_mtr_station = "UOT"; } // 東鐵綫 -> 預設大學
}
void setup() {
Serial.begin(115200);
// 初始化屏幕硬體
tft.begin();
tft.setRotation(0); // 直向 320x480
// 初始化 LVGL 圖形引擎
lv_init();
lv_disp_draw_buf_init(&draw_buf, buf, NULL, 320 * 10);
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = 320;
disp_drv.ver_res = 480;
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
// 🌐 動態連接家中的 WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { delay(500); }
// ==========================================
// 🎨 UI 佈局構建 (上半部地鐵,下半部巴士)
// ==========================================
// 1. 頂部 MTR 路線切換列
static const char * btnm_map[] = {"Tuen Ma", "East Rail", ""};
lv_obj_t * btnm = lv_btnmatrix_create(lv_scr_act());
lv_btnmatrix_set_map(btnm, btnm_map);
lv_obj_set_size(btnm, 300, 40);
lv_obj_align(btnm, LV_ALIGN_TOP_MID, 0, 10);
lv_obj_add_event_cb(btnm, mtr_line_cb, LV_EVENT_VALUE_CHANGED, NULL);
// 2. 港鐵時間看板
lv_obj_t * mtr_box = lv_obj_create(lv_scr_act());
lv_obj_set_size(mtr_box, 300, 150);
lv_obj_align(mtr_box, LV_ALIGN_TOP_MID, 0, 60);
lv_obj_t * mtr_label = lv_label_create(mtr_box);
lv_label_set_text(mtr_label, "MTR Status:\nConnecting API...");
lv_obj_align(mtr_label, LV_ALIGN_CENTER, 0, 0);
// 3. 下半部巴士自訂專區
lv_obj_t * bus_box = lv_obj_create(lv_scr_act());
lv_obj_set_size(bus_box, 300, 180);
lv_obj_align(bus_box, LV_ALIGN_BOTTOM_MID, 0, -20);
// 巴士號碼輸入框 (TextArea)
lv_obj_t * ta = lv_textarea_create(bus_box);
lv_textarea_set_one_line(ta, true);
lv_textarea_set_text(ta, current_bus_route.c_str());
lv_obj_set_size(ta, 120, 40);
lv_obj_align(ta, LV_ALIGN_TOP_LEFT, 10, 10);
// 彈出虛擬鍵盤觸發
lv_obj_t * kb = lv_keyboard_create(lv_scr_act());
lv_obj_set_size(kb, 320, 160);
lv_obj_align(kb, LV_ALIGN_BOTTOM_MID, 0, 0);
lv_keyboard_set_textarea(kb, ta);
lv_obj_add_event_cb(kb, kb_event_cb, LV_EVENT_READY, NULL);
}
void loop() {
lv_timer_handler(); // 持續保持觸控與 UI 渲染
delay(5);
}