diff --git a/handshake-capture-c6/LVGL_Driver.cpp b/handshake-capture-c6/LVGL_Driver.cpp index 5117601..cafa31f 100644 --- a/handshake-capture-c6/LVGL_Driver.cpp +++ b/handshake-capture-c6/LVGL_Driver.cpp @@ -8,6 +8,8 @@ static lv_obj_t *list_label = nullptr; static lv_obj_t *network_container = nullptr; static lv_obj_t *network_labels[20] = {nullptr}; static int network_count = 0; +static lv_obj_t *progress_bar = nullptr; +static lv_obj_t *progress_label = nullptr; void Ui_SetWifiStatus(const char *text) { if (status_label != nullptr) { @@ -51,6 +53,32 @@ void Ui_SetNetworkText(int index, const char* text) { lv_label_set_text(network_labels[index], text); } +void Ui_SetProgress(int current, int total) { + if (progress_bar == nullptr) return; + if (total <= 0) { + lv_bar_set_value(progress_bar, 0, LV_ANIM_OFF); + if (progress_label) lv_label_set_text(progress_label, ""); + return; + } + int pct = (current * 100) / total; + lv_bar_set_value(progress_bar, pct, LV_ANIM_ON); + + char buf[16]; + snprintf(buf, sizeof(buf), "%d/%d", current, total); + if (progress_label) lv_label_set_text(progress_label, buf); +} + +void Ui_ShowProgress(bool visible) { + if (progress_bar == nullptr) return; + if (visible) { + lv_obj_clear_flag(progress_bar, LV_OBJ_FLAG_HIDDEN); + if (progress_label) lv_obj_clear_flag(progress_label, LV_OBJ_FLAG_HIDDEN); + } else { + lv_obj_add_flag(progress_bar, LV_OBJ_FLAG_HIDDEN); + if (progress_label) lv_obj_add_flag(progress_label, LV_OBJ_FLAG_HIDDEN); + } +} + void Lvgl_Display_LCD(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) { LCD_addWindow(area->x1, area->y1, area->x2, area->y2, (uint16_t*)&color_p->full); lv_disp_flush_ready(disp_drv); @@ -88,21 +116,46 @@ void Lvgl_Init(void) { lv_obj_set_style_bg_color(lv_scr_act(), lv_color_black(), 0); lv_obj_set_style_bg_opa(lv_scr_act(), LV_OPA_COVER, 0); + // Title lv_obj_t *title = lv_label_create(lv_scr_act()); lv_obj_set_style_text_color(title, lv_palette_main(LV_PALETTE_GREEN), 0); lv_obj_set_style_text_font(title, &lv_font_montserrat_16, 0); - lv_label_set_text(title, "WiFi Networks"); - lv_obj_align(title, LV_ALIGN_TOP_LEFT, 10, 8); + lv_label_set_text(title, LV_SYMBOL_WIFI " Capture"); + lv_obj_align(title, LV_ALIGN_TOP_LEFT, 10, 6); + // Status line status_label = lv_label_create(lv_scr_act()); lv_obj_set_style_text_color(status_label, lv_palette_lighten(LV_PALETTE_GREEN, 2), 0); lv_obj_set_style_text_font(status_label, &lv_font_montserrat_12, 0); lv_label_set_text(status_label, "Starting..."); - lv_obj_align(status_label, LV_ALIGN_TOP_LEFT, 10, 30); + lv_obj_align(status_label, LV_ALIGN_TOP_LEFT, 10, 28); + // Progress bar (hidden until capture starts) + progress_bar = lv_bar_create(lv_scr_act()); + lv_obj_set_size(progress_bar, 114, 6); + lv_obj_align(progress_bar, LV_ALIGN_TOP_LEFT, 10, 44); + lv_bar_set_range(progress_bar, 0, 100); + lv_bar_set_value(progress_bar, 0, LV_ANIM_OFF); + lv_obj_set_style_bg_color(progress_bar, lv_color_make(25, 25, 25), LV_PART_MAIN); + lv_obj_set_style_bg_opa(progress_bar, LV_OPA_COVER, LV_PART_MAIN); + lv_obj_set_style_bg_color(progress_bar, lv_palette_main(LV_PALETTE_GREEN), LV_PART_INDICATOR); + lv_obj_set_style_bg_opa(progress_bar, LV_OPA_COVER, LV_PART_INDICATOR); + lv_obj_set_style_radius(progress_bar, 2, LV_PART_MAIN); + lv_obj_set_style_radius(progress_bar, 2, LV_PART_INDICATOR); + lv_obj_add_flag(progress_bar, LV_OBJ_FLAG_HIDDEN); + + // Progress count label (right of bar) + progress_label = lv_label_create(lv_scr_act()); + lv_obj_set_style_text_color(progress_label, lv_palette_main(LV_PALETTE_GREEN), 0); + lv_obj_set_style_text_font(progress_label, &lv_font_montserrat_12, 0); + lv_label_set_text(progress_label, ""); + lv_obj_align(progress_label, LV_ALIGN_TOP_LEFT, 130, 41); + lv_obj_add_flag(progress_label, LV_OBJ_FLAG_HIDDEN); + + // Network list panel lv_obj_t *list_panel = lv_obj_create(lv_scr_act()); - lv_obj_set_size(list_panel, 164, 260); - lv_obj_align(list_panel, LV_ALIGN_TOP_LEFT, 4, 52); + lv_obj_set_size(list_panel, 164, 254); + lv_obj_align(list_panel, LV_ALIGN_TOP_LEFT, 4, 54); lv_obj_set_style_bg_color(list_panel, lv_color_black(), 0); lv_obj_set_style_bg_opa(list_panel, LV_OPA_TRANSP, 0); lv_obj_set_style_border_width(list_panel, 1, 0); diff --git a/handshake-capture-c6/LVGL_Driver.h b/handshake-capture-c6/LVGL_Driver.h index ec43abe..780c623 100644 --- a/handshake-capture-c6/LVGL_Driver.h +++ b/handshake-capture-c6/LVGL_Driver.h @@ -15,3 +15,5 @@ void Ui_ClearNetworkList(void); void Ui_AddNetwork(const char* text, int index); void Ui_SetNetworkColor(int index, lv_color_t color); void Ui_SetNetworkText(int index, const char* text); +void Ui_SetProgress(int current, int total); +void Ui_ShowProgress(bool visible); diff --git a/handshake-capture-c6/handshake-capture-c6.ino b/handshake-capture-c6/handshake-capture-c6.ino index a9ae993..24189d4 100644 --- a/handshake-capture-c6/handshake-capture-c6.ino +++ b/handshake-capture-c6/handshake-capture-c6.ino @@ -1,7 +1,7 @@ /* * WiFi Handshake Capture - ESP32-C6 1.47" LCD - * Multi-target capture: up to 3 networks per channel, deauth all, capture all. - * Display: green = not captured, red = captured. + * Multi-target capture: up to 3 networks per channel. + * Green = pending, orange blink = active target, red = captured. */ #include "Display_ST7789.h" @@ -20,6 +20,90 @@ CaptureState captureState = STATE_SCANNING; unsigned long capture_start_time = 0; const unsigned long CHANNEL_TIMEOUT_MS = 30000; +// ─── Live capture visual update ─── + +void updateCaptureVisuals() { + static unsigned long last_vis = 0; + static bool blink = false; + + unsigned long now = millis(); + if (now - last_vis < 400) return; + last_vis = now; + blink = !blink; + + // Overall progress: captured / total WPA targets + int captured = 0, total = 0; + for (int i = 0; i < MAX_NETWORKS; i++) { + if (networks[i].ssid.isEmpty()) break; + if (!isCaptureable(i)) continue; + total++; + if (networks[i].handshake_captured) captured++; + } + Ui_SetProgress(captured, total); + + // Per-network visual state + for (int i = 0; i < MAX_NETWORKS; i++) { + if (networks[i].ssid.isEmpty()) break; + + // Is this network in an active capture slot? + int si = -1; + for (int s = 0; s < MAX_SLOTS; s++) { + if (slots[s].active && slots[s].network_index == i) { + si = s; + break; + } + } + + char buf[48]; + + if (networks[i].handshake_captured) { + // ✓ captured — red, static + snprintf(buf, sizeof(buf), LV_SYMBOL_OK " %s", networks[i].ssid.c_str()); + Ui_SetNetworkText(i, buf); + Ui_SetNetworkColor(i, lv_palette_main(LV_PALETTE_RED)); + } + else if (si >= 0) { + // ⚡ active target — orange, blinking + CaptureSlot* slot = &slots[si]; + if (capturePhase == PHASE_OBSERVING) { + snprintf(buf, sizeof(buf), LV_SYMBOL_CHARGE " %s +%dc", + slot->ssid, slot->client_count); + } else { + snprintf(buf, sizeof(buf), LV_SYMBOL_CHARGE " %s %d/2", + slot->ssid, slot->eapol_count); + } + Ui_SetNetworkText(i, buf); + + lv_color_t color = blink ? + lv_palette_main(LV_PALETTE_ORANGE) : + lv_palette_darken(LV_PALETTE_ORANGE, 2); + Ui_SetNetworkColor(i, color); + } + // else: pending — stays green from scan, no update needed + } + + // Status line reflects current phase + static CapturePhase last_phase = PHASE_CAPTURING; + if (capturePhase != last_phase) { + if (capturePhase == PHASE_OBSERVING) + Ui_SetWifiStatus("Mapping clients..."); + else + Ui_SetWifiStatus("Capturing..."); + last_phase = capturePhase; + } + + static int last_eapol = -1; + if (capturePhase == PHASE_CAPTURING && latest_eapol_count != last_eapol) { + char ebuf[32]; + snprintf(ebuf, sizeof(ebuf), "Ch %d " LV_SYMBOL_CHARGE " EAPOL %d", + current_channel, latest_eapol_count); + Ui_SetWifiStatus(ebuf); + last_eapol = latest_eapol_count; + } +} + +// ─── Setup ─── + void setup() { Serial.begin(115200); LCD_Init(); @@ -37,6 +121,8 @@ void setup() { captureState = STATE_SCANNING; } +// ─── Main loop ─── + void loop() { Timer_Loop(); handshakeCaptureProcessPending(); @@ -44,6 +130,7 @@ void loop() { switch (captureState) { case STATE_SCANNING: { + Ui_ShowProgress(false); if (getCaptureableCount() == 0) { Ui_SetWifiStatus("Rescan"); WiFiScanner_Refresh(); @@ -54,7 +141,8 @@ void loop() { if (is_capturing) { captureState = STATE_CAPTURING; capture_start_time = millis(); - Ui_SetWifiStatus("Mapping clients"); + Ui_ShowProgress(true); + Ui_SetWifiStatus("Mapping clients..."); } else { Ui_SetWifiStatus("No targets"); delay(1000); @@ -63,6 +151,8 @@ void loop() { } case STATE_CAPTURING: { + updateCaptureVisuals(); + bool any_active = false; for (int i = 0; i < MAX_SLOTS; i++) { if (slots[i].active) { @@ -71,23 +161,8 @@ void loop() { } } - // Phase-aware status display - static CapturePhase last_shown_phase = PHASE_CAPTURING; - if (capturePhase != last_shown_phase) { - Ui_SetWifiStatus(capturePhase == PHASE_OBSERVING ? "Mapping clients" : "Capturing..."); - last_shown_phase = capturePhase; - } - - static int last_eapol = -1; - if (capturePhase == PHASE_CAPTURING && latest_eapol_count != last_eapol) { - char buf[64]; - snprintf(buf, sizeof(buf), "EAPOL %d/2", latest_eapol_count); - Ui_SetWifiStatus(buf); - last_eapol = latest_eapol_count; - } - if (!any_active && getCaptureableCount() == 0) { - Ui_SetWifiStatus("Done"); + Ui_SetWifiStatus(LV_SYMBOL_OK " All done"); stopCapture(); captureState = STATE_SCANNING; delay(2000);