/* * WiFi Handshake Capture - ESP32-C6 1.47" LCD * Multi-target capture: up to 2 networks per channel (deauth + wait, rotate until all done). * Green = pending, orange blink = active target, red = captured. */ #include "Display_ST7789.h" #include "LVGL_Driver.h" #include "WiFi_Scanner.h" #include "SD_Card.h" #include "HandshakeCapture.h" enum CaptureState { STATE_SCANNING, STATE_CAPTURING }; CaptureState captureState = STATE_SCANNING; unsigned long capture_start_time = 0; // ─── 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/%d", slot->ssid, (int)slot->eapol_count, (int)HANDSHAKE_EAPOL_FRAMES); } 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 uint32_t last_eapol_sig = 0xffffffffu; uint32_t sig = 0; for (int s = 0; s < MAX_SLOTS; s++) sig |= (uint32_t)latest_eapol_count[s] << (8 * s); if (capturePhase == PHASE_CAPTURING && sig != last_eapol_sig) { char ebuf[48]; int p = snprintf(ebuf, sizeof(ebuf), "Ch%d " LV_SYMBOL_CHARGE " E", current_channel); for (int s = 0; s < MAX_SLOTS && p < (int)sizeof(ebuf) - 6; s++) { p += snprintf(ebuf + p, sizeof(ebuf) - (size_t)p, "%s%d", s ? "/" : "", (int)latest_eapol_count[s]); } Ui_SetWifiStatus(ebuf); last_eapol_sig = sig; } } // ─── Setup ─── void setup() { Serial.begin(115200); delay(200); Serial.printf("handshake-capture-c6 v%s\n", HANDSHAKE_FIRMWARE_VERSION); LCD_Init(); Set_Backlight(100); Lvgl_Init(); // WiFi up before SD (shared SPI); first scan only after persistence load so reds match SD file. handshakeCaptureInit(); HANDSHAKE_LOGLN("[SETUP] mounting SD after WiFi init"); if (!SD_Init()) { Ui_SetWifiStatus("SD: no card"); WiFiScanner_Refresh(); } else { Ui_SetWifiStatus("SD OK"); handshakeLoadPersistedBssids(); WiFiScanner_Refresh(); } captureState = STATE_SCANNING; } // ─── Main loop ─── void loop() { Timer_Loop(); handshakeCaptureProcessPending(); handshakeCaptureLoop(); switch (captureState) { case STATE_SCANNING: { Ui_ShowProgress(false); if (getCaptureableCount() == 0) { Ui_SetWifiStatus("Rescan"); WiFiScanner_Refresh(); delay(500); break; } if (!SD_IsReady()) { HANDSHAKE_LOGLN("[CAP] SD not ready — calling SD_Init before capture"); SD_Init(); if (!SD_IsReady()) { HANDSHAKE_LOGLN("[CAP] BLOCKED: SD_IsReady still false — UI: Insert SD card"); Ui_SetWifiStatus("Insert SD card"); delay(800); break; } } startMultiCapture(); if (is_capturing) { captureState = STATE_CAPTURING; capture_start_time = millis(); Ui_ShowProgress(true); Ui_SetWifiStatus("Mapping clients..."); } else { Ui_SetWifiStatus("No targets"); delay(1000); } break; } case STATE_CAPTURING: { updateCaptureVisuals(); bool any_active = false; for (int i = 0; i < MAX_SLOTS; i++) { if (slots[i].active) { any_active = true; break; } } if (!any_active && getCaptureableCount() == 0) { Ui_SetWifiStatus(LV_SYMBOL_OK " All done"); stopCapture(); captureState = STATE_SCANNING; delay(2000); } else if (!any_active) { stopCapture(); captureState = STATE_SCANNING; } else if (millis() - capture_start_time > HANDSHAKE_CHANNEL_TIMEOUT_MS) { Ui_SetWifiStatus("Ch timeout"); stopCapture(); captureState = STATE_SCANNING; delay(2000); } break; } } delay(16); }