# Firmware Performance Improvements Review of handshake-capture-c6 for ESP32-C6 1.47" LCD. Ordered by impact and effort. --- ## 1. Promiscuous Callback — Reduce Critical Section Scope **File:** `HandshakeCapture.cpp` ~lines 352–385 **Issue:** The entire slot loop runs inside `portENTER_CRITICAL` / `portEXIT_CRITICAL`. For every frame we hold the spinlock across up to 3 slot checks, each with several `memcmp` calls. This blocks all other cores/tasks and can cause missed packets at high traffic. **Change:** Shrink the critical section to only the minimal shared writes: - Copy `slots`, `pending_save_slots`, `is_capturing`, `capturePhase` into locals at callback entry. - Do all reads and logic outside the lock. - Enter critical only for: `pcapAppendSlot`, `addClientToSlot`, `pending_save_slots[i] = i`, `slot->eapol_count++`, `slot->client_count` updates, `latest_eapol_count`. **Effort:** Medium | **Impact:** High (fewer dropped frames) --- ## 2. Early-Exit in Callback for Non-Target Frames **File:** `HandshakeCapture.cpp` ~lines 320–385 **Issue:** For every data frame we iterate all 3 slots and do BSSID comparisons even when the frame has nothing to do with our targets. On busy channels that’s a lot of wasted `memcmp`. **Change:** Build a quick-reject set: hash or simple bloom of our target BSSIDs. If neither Addr1 nor Addr2 matches any target, `continue` before the slot loop. Alternatively, compare against slot BSSIDs only when frame type is beacon or data, and bail immediately when no match. **Effort:** Low | **Impact:** Medium (less CPU per frame) --- ## 3. Staggered Deauth Bursts **File:** `HandshakeCapture.cpp` ~`sendDeauthBurstAll`, `sendDeauthToClient` **Issue:** All slots are deauthed back-to-back. Radio TX blinds RX; a burst of many deauths increases the chance we miss the following EAPOL. **Change:** Interleave deauth with short RX gaps: - 5 frames to client A → 50–100ms delay → 5 frames to client B → delay → etc. - Or: 1 frame per client, round-robin, 2–3 rounds with short gaps. **Effort:** Low | **Impact:** Medium–High (better EAPOL capture window) --- ## 4. PMKID-First Immediate Save **File:** `HandshakeCapture.cpp` promiscuous callback, EAPOL branch **Issue:** We only set `pending_save_slots[i] = i` when `eapol_count >= 2`. PMKID is in EAPOL Message 1; hashcat can use it alone. If we never see M2, we discard usable data. **Change:** On first EAPOL (M1), parse key info byte; if PMKID flag is set, set `pending_save_slots[i] = i` immediately. Keep current logic for M2 as well. Ensures we don’t drop PMKID-only handshakes. **Effort:** Low | **Impact:** High (more crackable captures) --- ## 5. Replace `String` with Fixed Buffers in Hot Paths **File:** `HandshakeCapture.h` `WifiNetwork`, `HandshakeCapture.cpp` `handshakeCaptureProcessPending` ~line 89 **Issue:** `String` uses dynamic allocation. In `handshakeCaptureProcessPending`, `filename += s->ssid` and similar can fragment heap. `WifiNetwork::ssid` and `encryption` as `String` add overhead in scan/capture paths. **Change:** Use `char ssid[33]` and `char encryption[16]` in `WifiNetwork`. In `handshakeCaptureProcessPending`, use `snprintf` into a stack buffer for the filename. Reduces heap use and allocation during capture. **Effort:** Medium | **Impact:** Medium (stability, less heap pressure) --- ## 6. SD Write Off Main Loop **File:** `HandshakeCapture.cpp` `handshakeCaptureProcessPending` **Issue:** SD writes block the main loop. A large PCAP (e.g. 4KB) can take 50–200ms; during that time `handshakeCaptureLoop` doesn’t run, LVGL stutters, and we might miss phase transitions. **Change:** Defer SD write to a lower-priority task or timer callback. `handshakeCaptureProcessPending` only marks a slot “ready to write” and copies its buffer to a secondary buffer. A separate task or `loop()`-called `processSdQueue()` does the actual `SD.open` / `write` / `close`. Alternatively, use non-blocking SPI if available. **Effort:** Medium–High | **Impact:** Medium (smoother UI, fewer timing glitches) --- ## 7. Channel Hop Strategy — Full Sweep **File:** `HandshakeCapture.cpp` `getBestChannel`, `handshake-capture-c6.ino` channel logic **Issue:** We stay on the single “best” channel (most targets) for the full timeout. APs on ch 2–5, 7–10, 12–14 are never visited. **Change:** Maintain a channel list sorted by target count. After exhausting retries on channel N, move to channel N+1 instead of rescanning. Rotate through all channels that have uncaptured WPA targets. Reduces time spent on empty channels and improves coverage. **Effort:** Medium | **Impact:** High (more handshakes per run) --- ## 8. RSSI-Based Slot Ordering **File:** `HandshakeCapture.cpp` `startMultiCapture`, `scanNetworksSortedByRSSI` **Issue:** Slots are filled in scan order. Stronger-signal APs often have faster reconnects and easier captures. **Change:** When filling slots, sort `networks` by RSSI for the current channel, then assign slots. Prioritize high-RSSI targets. Optional: during observation, weight client mapping by RSSI. **Effort:** Low | **Impact:** Low–Medium (better success on marginal APs) --- ## 9. LVGL Update Throttling **File:** `handshake-capture-c6.ino` `updateCaptureVisuals` **Issue:** We iterate all networks and update LVGL every 400ms. When there are many networks, this does a lot of `lv_label_set_text` and `lv_obj_set_style_text_color` per cycle. **Change:** Track `last_updated_index` and only refresh labels that actually changed (compare new text/color to previous). Batch LVGL updates or skip when `captureState != STATE_CAPTURING`. **Effort:** Low | **Impact:** Low (smoother UI, less CPU) --- ## 10. `wasBssidCaptured` Linear Scan **File:** `HandshakeCapture.cpp` `wasBssidCaptured`, `scanNetworksSortedByRSSI` ~line 219 **Issue:** `wasBssidCaptured` is O(n) per network during scan. With 20 networks and 32 cached BSSIDs, that’s up to 640 comparisons per scan. **Change:** Use a small hash set or sorted array + binary search for the BSSID cache. Or cache the last few lookups. For 32 entries, linear is acceptable; optimization is only if scan becomes a bottleneck. **Effort:** Low | **Impact:** Low --- ## 11. Progress Bar Animation **File:** `LVGL_Driver.cpp` `Ui_SetProgress` ~line 64 **Issue:** `lv_bar_set_value(..., LV_ANIM_ON)` triggers an animation every 400ms when `updateCaptureVisuals` runs, even when the value is unchanged. **Change:** Only call `lv_bar_set_value` when `pct` or `current`/`total` actually changed. Use `LV_ANIM_OFF` for progress bar to avoid unnecessary redraws. **Effort:** Trivial | **Impact:** Low (less LVGL work) --- ## 12. Disable Wi-Fi AP Mode When Not Needed **File:** `HandshakeCapture.cpp` `handshakeCaptureInit` **Issue:** `WiFi.mode(WIFI_AP_STA)` keeps the soft AP up. You’ve said no web interface; the AP may be unnecessary and draws power. **Change:** Use `WIFI_STA` only if no AP functionality is required. Verify promiscuous mode works in STA-only. If it does, drop AP to save power. **Effort:** Low | **Impact:** Low (power, simpler state) --- ## 13. `millis()` in Critical Section **File:** `HandshakeCapture.cpp` `pcapAppendSlot` ~line 249 **Issue:** `millis()` is called inside the spinlock-protected `pcapAppendSlot`. `millis()` can have non-trivial cost on some platforms. **Change:** Call `millis()` once at callback entry, store in a local, pass it into `pcapAppendSlot` as a parameter. Keeps the critical section shorter. **Effort:** Trivial | **Impact:** Low --- ## 14. `WiFiScanner_Init` Unused **File:** `WiFi_Scanner.cpp` defines `WiFiScanner_Init`; it’s never called from the main sketch. **Change:** Remove dead code or call it from `setup()` if it’s meant to configure WiFi before scanning. **Effort:** Trivial | **Impact:** None (cleanup) --- ## 15. Scan: Hidden Network Support **File:** `HandshakeCapture.cpp` `scanNetworksSortedByRSSI` ~line 205 **Issue:** `if (ssid.isEmpty()) continue` skips hidden networks entirely. **Change:** Use `WiFi.scanNetworks(true, true)` to include hidden, and use a placeholder like `""` for empty SSID. Store BSSID and channel; we can still target by BSSID. Requires handling `` in filenames and UI. **Effort:** Low | **Impact:** Medium (more targets) --- ## Quick Wins (minimal code, clear benefit) | # | Change | File | |---|--------|------| | 3 | Stagger deauth with RX gaps | HandshakeCapture.cpp | | 4 | PMKID-first save on M1 | HandshakeCapture.cpp | | 8 | RSSI-sort slots in `startMultiCapture` | HandshakeCapture.cpp | | 11 | Only update progress bar when value changes | LVGL_Driver.cpp | | 13 | Pass `millis()` into `pcapAppendSlot` | HandshakeCapture.cpp | --- ## Higher Effort, Higher Impact | # | Change | |---|--------| | 1 | Shrink callback critical section | | 5 | Replace String with fixed buffers | | 6 | Offload SD write from main loop | | 7 | Full channel sweep instead of single best |