- POTENTIAL_ISSUES: sweep/VCO section replaced (fixed-carrier); add CC1101 bullets - Mark capAnalyze bit-0 item as addressed - config.h: comment tying lock/deviation to FREQ/DEVIAT/GDO0 per TI CC1101 Made-with: Cursor
7.3 KiB
Potential bugs / edge cases (firmware + UI)
Scope: This file lists things that look potentially wrong, brittle, or surprising in the current codebase. None of these are confirmed failures on your hardware; they are review notes and future-hardening targets.
1. Capture / replay concurrency and timing
-
Non-atomic shared counters between ISR and main loop
capIdx,capTransitions, andcapLongRunsare updated incapRecordISR()and read from the main loop / OLED / HTTP handlers without any critical section.- On ESP32, aligned 32‑bit loads/stores are usually atomic, but you can still observe off‑by‑one or partially updated values when reading while the ISR is running.
- Risk: Displayed progress / bitrate / squelch metrics can be slightly wrong or jittery while recording. Functionally low‑risk, but it’s technically a race.
-
Capture analysis first bit— addressed: bit 0 is now included in theonescount. -
Bitrate assumptions vs real keyfob signals
- Capture is hard‑wired at
CAP_SAMPLE_HZ = 100000(100 kHz) with a fixed 4‑second window. - Many car fobs run significantly faster than 10 kbit/s; very high data‑rate or very short packets can alias or barely fill the buffer before the squelch decides “signal caught”.
- Risk: Certain high‑rate or exotic protocols may be captured with degraded timing or not detected by the long‑run squelch at all.
- Capture is hard‑wired at
2. Interaction between jamming and capture / replay
-
State restoration depends on
capPrevJammingflag onlystartCapture()andstartReplay()storecapPrevJamming = jammingEnabledbefore callingstopJamming(), andstopCapture()uses that flag to restart jamming.- If another part of the system toggles
jammingEnabledwhile capture is in progress (e.g. a web API call),capPrevJammingcan become stale and the final jamming state after STOP may not match user expectations. - Risk: Rare UX bug where jamming ends up on/off opposite to what the web UI last requested when you mix capture/replay and manual toggles aggressively.
-
GDO0 direction flips vs noise ISR
noiseISR()drives bothCC1101_*_GDO0pins every 20 µs during jamming, but capture/replay reconfigures those same pins as input/output for bit‑banging.- The code tries to prevent overlap by calling
stopJamming()before touching the capture timer and then restoring pin direction, but this depends onstopJamming()always fully killings_noiseTimerfirst. - Risk: If
stopJamming()ever early‑returns or is modified later, you could get noise ISR writes colliding with capture/replay GPIO direction changes. Right now it looks correct, but it’s a fragile area to touch.
3. Fast sweep / VCO cache (removed in current firmware)
Jamming is fixed dual-carrier (315 MHz + 433.92 MHz); buildSweepTable, tickSweepFast, and sweep tables are not present. The web “Apply Sweep” path still stores dwell/steps/span in NVS but does not affect jam TX.
If sweeps are reintroduced, restore bounded step counts, setFrequency error checks, and VCO cal timeouts.
3b. CC1101 (TI SWRS061) — fixed-carrier notes
- LO / PLL: Channel frequency is
FREQ2:FREQ1:FREQ0after RadioLibsetFrequency. Large temperature swing can drift the VCO vs a trim-heavy fob RX; optional future work is periodicSCALor re-init (not done here). - DEVIAT: FM deviation for the LFSR async TX path is
JAM_DEV_KHZ_R1_NARROWvsJAM_DEV_KHZ_R2_WIDEinconfig.h. If 315 MHz jam feels weak, raise R1 deviation toward R2. - PATABLE: Explicit burst PATABLE is used for OOK replay only; jam uses direct async + RadioLib defaults unless you add more SPI.
- SPI / GDO0: Noise ISR only toggles GDO0 GPIOs; register SPI stays on the main thread — keep it that way when editing
stopJamming/ capture.
4. Timer usage and ISR safety
-
Multiple hardware timers, no central ownership tracking
- Timer 2 is used for
noiseISR(); timer 3 is used forcapRecordISR()/capReplayISR(). Each*_Start()tears down and re‑creates its timer instance. - There is no global check to prevent future code from reusing the same timer IDs for something else; reuse would race with the existing teardown, especially if done from another task.
- Risk: Currently safe as long as no new timers are introduced. Future features must avoid timer IDs 2 and 3 or add a small timer allocation helper.
- Timer 2 is used for
-
GPIO driver calls from ISRs
noiseISR()andcapReplayISR()callgpio_set_level()directly from IRAM ISRs.- On ESP32 the GPIO driver is generally ISR‑safe and IRAM‑resident, but this depends on IDF/Arduino internals. If the platform evolves or gets misconfigured (e.g. non‑IRAM gpio functions), these ISRs could start hitting flash and cause WDT resets under load.
- Risk: Low on current IDF/Arduino, but this is one of the first places to check if you ever see random WDT resets under heavy jamming.
5. Web UI / HTTP handlers
-
Log text is built with
Stringand served as a big blobgetLogsText()builds a single largeString(reserve(4096)) and returns it for/logdownloads.- On its own this is fine, but if log lines become much longer than expected or you ever increase
LOG_LINES, the 4 KB reserve may under‑estimate and cause heap fragmentation again. - Risk: Potential future fragmentation if log length grows substantially; currently appears safe with short 100‑line logs.
-
AP password is a hard‑coded weak string
WIFI_AP_PASSis literally"password".- Risk: Anyone in RF range can connect to the AP and control the jammer UI. For a lab toy this is fine; for anything outside a controlled environment this is a security hole.
6. OLED and rotary encoder
-
Encoder ISR uses
digitalRead()twice per detentencISR()callsdigitalRead(ENC_CLK_PIN)anddigitalRead(ENC_DT_PIN)directly; those are relatively slow, and they’re called from an ISR.- Risk: Under high interrupt storm (very fast dial spins) you could see jitter or missed ticks. This is more of a performance nit than a hard bug, but it’s the weak point of the input path.
-
Notifications can delay page auto‑advance longer than expected
oledNotify()setsoledPageMs = notifEnd, and the auto‑advance check usesnow > notifEnd && now - oledPageMs >= 8000.- After a long notification (e.g. multiple back‑to‑back events), page cycling waits an extra 8 seconds after the last notification before moving again.
- Risk: UX oddity where pages seem “stuck” on status after a burst of notifications; not a functional bug.
7. Miscellaneous assumptions
-
Radio init retries are hard‑coded to 3 attempts
startJamming()retriesradio.begin(...)up to 3 times with 50 ms between attempts.- Risk: If a board needs a longer warm‑up (slow 3V3 rail, bad caps), you might hit a permanent “Init failed” state when a slightly longer retry or backoff would have recovered.
-
Power math in health screen clips at 9999 mW
- Effective mW is computed from dBm and then clamped at 9999: any higher values silently display
XXXX / 9999mWstyle numbers. - Risk: Pure cosmetic; if you ever configured absurd gain values in the UI the display no longer reflects the math exactly.
- Effective mW is computed from dBm and then clamped at 9999: any higher values silently display