Files
drjones 804e71e175 handshake-capture-c6: production SD gating (SD_IsReady), v1.0.1
- Gate capture on SD_IsReady() after successful SD_Init; avoid false
  'insert SD' when SD.cardType() lies after WiFi on shared SPI with LCD
- SD_Init: LCD CS high, SD.end() on empty card; HandshakeCapture save path
  retries SD_Init before discard
- FAT filename sanitize, discard deadlocks, WPA3 scan labels, docs (README,
  PRODUCTION, IMPROVEMENTS), firmware version 1.0.1

Made-with: Cursor
2026-03-21 01:12:12 -07:00

212 lines
9.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Firmware Performance Improvements
> **Production / release:** see **`README.md`** (Production checklist) and **`PRODUCTION.md`**. Bump **`HANDSHAKE_FIRMWARE_VERSION`** in `HandshakeCapture.h` for each field build.
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 352385
**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 320385
**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 thats 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 → 50100ms delay → 5 frames to client B → delay → etc.
- Or: 1 frame per client, round-robin, 23 rounds with short gaps.
**Effort:** Low | **Impact:** MediumHigh (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 dont 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 50200ms; during that time `handshakeCaptureLoop` doesnt 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:** MediumHigh | **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 25, 710, 1214 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:** LowMedium (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, thats 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. Youve 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`; its never called from the main sketch.
**Change:** Remove dead code or call it from `setup()` if its 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 `"<hidden>"` for empty SSID. Store BSSID and channel; we can still target by BSSID. Requires handling `<hidden>` 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 |