diff --git a/firmware/esp32-s3-dashboard/esp32-s3-dashboard.ino b/firmware/esp32-s3-dashboard/esp32-s3-dashboard.ino index 9ee39a8..7270f78 100644 --- a/firmware/esp32-s3-dashboard/esp32-s3-dashboard.ino +++ b/firmware/esp32-s3-dashboard/esp32-s3-dashboard.ino @@ -1,4 +1,6 @@ #include +#include +#include #include #include @@ -7,8 +9,33 @@ using namespace esp_panel::drivers; using namespace esp_panel::board; +static const char *DASHBOARD_VERSION = "0.0.2-wifi-status"; + +// Update these if your Cargo ESP AP credentials are different. +static const char *CARGO_WIFI_SSID = "OverlandController"; +static const char *CARGO_WIFI_PASSWORD = "overland1234"; + +static const char *CARGO_API_STATUS_URL = "http://192.168.4.1/api/v1/status"; + +static lv_obj_t *wifi_label = nullptr; +static lv_obj_t *api_label = nullptr; +static lv_obj_t *status_label = nullptr; static lv_obj_t *touch_count_label = nullptr; + static uint32_t touch_count = 0; +static unsigned long last_wifi_attempt_ms = 0; +static unsigned long last_status_poll_ms = 0; + +static void set_label_text(lv_obj_t *label, const String &text) +{ + if (label == nullptr) { + return; + } + + lvgl_port_lock(-1); + lv_label_set_text(label, text.c_str()); + lvgl_port_unlock(); +} static void touch_test_event_cb(lv_event_t *event) { @@ -19,14 +46,104 @@ static void touch_test_event_cb(lv_event_t *event) touch_count++; if (touch_count_label != nullptr) { + lvgl_port_lock(-1); lv_label_set_text_fmt(touch_count_label, "Touch test count: %lu", (unsigned long)touch_count); + lvgl_port_unlock(); } Serial.print("Touch test count: "); Serial.println(touch_count); } -static void create_overland_boot_screen() +static void connect_wifi_if_needed() +{ + if (WiFi.status() == WL_CONNECTED) { + return; + } + + if (millis() - last_wifi_attempt_ms < 10000) { + return; + } + + last_wifi_attempt_ms = millis(); + + Serial.print("Connecting to Cargo ESP AP: "); + Serial.println(CARGO_WIFI_SSID); + + set_label_text(wifi_label, "WiFi: connecting..."); + + WiFi.mode(WIFI_STA); + WiFi.begin(CARGO_WIFI_SSID, CARGO_WIFI_PASSWORD); +} + +static void update_wifi_label() +{ + if (WiFi.status() == WL_CONNECTED) { + String text = "WiFi: connected\nIP: "; + text += WiFi.localIP().toString(); + text += "\nRSSI: "; + text += String(WiFi.RSSI()); + text += " dBm"; + set_label_text(wifi_label, text); + } else { + set_label_text(wifi_label, "WiFi: not connected\nSSID: " + String(CARGO_WIFI_SSID)); + } +} + +static void poll_status_api() +{ + if (WiFi.status() != WL_CONNECTED) { + set_label_text(api_label, "API: waiting for WiFi"); + set_label_text(status_label, "No API response yet."); + return; + } + + if (millis() - last_status_poll_ms < 5000) { + return; + } + + last_status_poll_ms = millis(); + + Serial.print("GET "); + Serial.println(CARGO_API_STATUS_URL); + + set_label_text(api_label, "API: requesting /api/v1/status"); + + HTTPClient http; + http.begin(CARGO_API_STATUS_URL); + int code = http.GET(); + + if (code > 0) { + String body = http.getString(); + + Serial.print("HTTP "); + Serial.println(code); + Serial.println(body); + + String api_text = "API: HTTP "; + api_text += String(code); + api_text += "\nGET /api/v1/status"; + set_label_text(api_label, api_text); + + if (body.length() > 900) { + body = body.substring(0, 900); + body += "\n...truncated..."; + } + + set_label_text(status_label, body); + } else { + String error = "API request failed: "; + error += http.errorToString(code); + + Serial.println(error); + set_label_text(api_label, error); + set_label_text(status_label, "No valid response from Cargo ESP."); + } + + http.end(); +} + +static void create_overland_status_screen() { lv_obj_t *screen = lv_scr_act(); lv_obj_clean(screen); @@ -37,41 +154,61 @@ static void create_overland_boot_screen() lv_label_set_text(title, "Overland Controller"); lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0); lv_obj_set_style_text_font(title, &lv_font_montserrat_30, 0); - lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 40); + lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 24); lv_obj_t *subtitle = lv_label_create(screen); - lv_label_set_text(subtitle, "ESP32-S3 Dashboard Bring-Up"); + lv_label_set_text(subtitle, "ESP32-S3 Dashboard - WiFi/API Bring-Up"); lv_obj_set_style_text_color(subtitle, lv_color_hex(0xB8C0C8), 0); - lv_obj_set_width(subtitle, 760); + lv_obj_set_width(subtitle, 920); lv_obj_set_style_text_align(subtitle, LV_TEXT_ALIGN_CENTER, 0); - lv_obj_align_to(subtitle, title, LV_ALIGN_OUT_BOTTOM_MID, 0, 12); + lv_obj_align_to(subtitle, title, LV_ALIGN_OUT_BOTTOM_MID, 0, 8); - lv_obj_t *card = lv_obj_create(screen); - lv_obj_set_size(card, 760, 330); - lv_obj_align(card, LV_ALIGN_CENTER, 0, 25); - lv_obj_set_style_radius(card, 18, 0); - lv_obj_set_style_bg_color(card, lv_color_hex(0x1F2933), 0); - lv_obj_set_style_border_color(card, lv_color_hex(0x3A4652), 0); - lv_obj_set_style_border_width(card, 2, 0); - lv_obj_set_style_pad_all(card, 24, 0); + lv_obj_t *left_card = lv_obj_create(screen); + lv_obj_set_size(left_card, 430, 360); + lv_obj_align(left_card, LV_ALIGN_LEFT_MID, 38, 20); + lv_obj_set_style_radius(left_card, 16, 0); + lv_obj_set_style_bg_color(left_card, lv_color_hex(0x1F2933), 0); + lv_obj_set_style_border_color(left_card, lv_color_hex(0x3A4652), 0); + lv_obj_set_style_border_width(left_card, 2, 0); + lv_obj_set_style_pad_all(left_card, 18, 0); - lv_obj_t *status = lv_label_create(card); + lv_obj_t *right_card = lv_obj_create(screen); + lv_obj_set_size(right_card, 500, 360); + lv_obj_align(right_card, LV_ALIGN_RIGHT_MID, -38, 20); + lv_obj_set_style_radius(right_card, 16, 0); + lv_obj_set_style_bg_color(right_card, lv_color_hex(0x1F2933), 0); + lv_obj_set_style_border_color(right_card, lv_color_hex(0x3A4652), 0); + lv_obj_set_style_border_width(right_card, 2, 0); + lv_obj_set_style_pad_all(right_card, 18, 0); + + lv_obj_t *hardware_label = lv_label_create(left_card); lv_label_set_text( - status, - "Hardware: Waveshare ESP32-S3 Touch LCD 5B\n" - "Display: 1024 x 600\n" - "Touch: Capacitive\n" - "Cargo API Base: /api/v1\n" - "WiFi: Not connected yet\n" - "Mode: Local dashboard bring-up" + hardware_label, + "Hardware:\n" + "Waveshare ESP32-S3 Touch LCD 5B\n\n" + "Display:\n" + "1024 x 600\n\n" + "Cargo API Base:\n" + "/api/v1" ); - lv_obj_set_style_text_color(status, lv_color_hex(0xFFFFFF), 0); - lv_obj_set_style_text_line_space(status, 10, 0); - lv_obj_set_width(status, 700); - lv_obj_align(status, LV_ALIGN_TOP_LEFT, 0, 0); + lv_obj_set_style_text_color(hardware_label, lv_color_hex(0xFFFFFF), 0); + lv_obj_set_width(hardware_label, 380); + lv_obj_align(hardware_label, LV_ALIGN_TOP_LEFT, 0, 0); - lv_obj_t *button = lv_btn_create(card); - lv_obj_set_size(button, 260, 72); + wifi_label = lv_label_create(left_card); + lv_label_set_text(wifi_label, "WiFi: not connected"); + lv_obj_set_style_text_color(wifi_label, lv_color_hex(0xB8C0C8), 0); + lv_obj_set_width(wifi_label, 380); + lv_obj_align(wifi_label, LV_ALIGN_TOP_LEFT, 0, 175); + + api_label = lv_label_create(left_card); + lv_label_set_text(api_label, "API: waiting"); + lv_obj_set_style_text_color(api_label, lv_color_hex(0xB8C0C8), 0); + lv_obj_set_width(api_label, 380); + lv_obj_align(api_label, LV_ALIGN_TOP_LEFT, 0, 250); + + lv_obj_t *button = lv_btn_create(left_card); + lv_obj_set_size(button, 170, 54); lv_obj_align(button, LV_ALIGN_BOTTOM_LEFT, 0, 0); lv_obj_add_event_cb(button, touch_test_event_cb, LV_EVENT_CLICKED, nullptr); @@ -79,17 +216,28 @@ static void create_overland_boot_screen() lv_label_set_text(button_label, "Touch Test"); lv_obj_center(button_label); - touch_count_label = lv_label_create(card); + touch_count_label = lv_label_create(left_card); lv_label_set_text(touch_count_label, "Touch test count: 0"); lv_obj_set_style_text_color(touch_count_label, lv_color_hex(0xB8C0C8), 0); - lv_obj_align(touch_count_label, LV_ALIGN_BOTTOM_LEFT, 300, -22); + lv_obj_align(touch_count_label, LV_ALIGN_BOTTOM_LEFT, 190, -18); + + lv_obj_t *status_title = lv_label_create(right_card); + lv_label_set_text(status_title, "GET /api/v1/status"); + lv_obj_set_style_text_color(status_title, lv_color_hex(0xFFFFFF), 0); + lv_obj_align(status_title, LV_ALIGN_TOP_LEFT, 0, 0); + + status_label = lv_label_create(right_card); + lv_label_set_text(status_label, "No API response yet."); + lv_obj_set_style_text_color(status_label, lv_color_hex(0xDDE3EA), 0); + lv_obj_set_width(status_label, 455); + lv_obj_align(status_label, LV_ALIGN_TOP_LEFT, 0, 34); lv_obj_t *footer = lv_label_create(screen); - lv_label_set_text(footer, "Next: WiFi client -> GET /api/v1/status from Cargo ESP"); + lv_label_set_text(footer, "Current milestone: Dashboard WiFi client -> Cargo ESP /api/v1/status"); lv_obj_set_style_text_color(footer, lv_color_hex(0x7D8996), 0); lv_obj_set_width(footer, 900); lv_obj_set_style_text_align(footer, LV_TEXT_ALIGN_CENTER, 0); - lv_obj_align(footer, LV_ALIGN_BOTTOM_MID, 0, -28); + lv_obj_align(footer, LV_ALIGN_BOTTOM_MID, 0, -18); } void setup() @@ -101,6 +249,8 @@ void setup() Serial.println("================================"); Serial.println("Overland Controller Dashboard"); Serial.println("================================"); + Serial.print("Version: "); + Serial.println(DASHBOARD_VERSION); Serial.println("Target: Waveshare ESP32-S3 Touch LCD 5B"); Serial.println("Display: 1024x600"); Serial.println("API Base: /api/v1"); @@ -125,15 +275,19 @@ void setup() Serial.println("Initializing LVGL"); lvgl_port_init(board->getLCD(), board->getTouch()); - Serial.println("Creating Overland boot screen"); + Serial.println("Creating Overland WiFi/API screen"); lvgl_port_lock(-1); - create_overland_boot_screen(); + create_overland_status_screen(); lvgl_port_unlock(); - Serial.println("Dashboard boot screen ready"); + Serial.println("Dashboard WiFi/API screen ready"); } void loop() { - delay(1000); + connect_wifi_if_needed(); + update_wifi_label(); + poll_status_api(); + + delay(500); }