diff --git a/src/main.cpp b/src/main.cpp index 095cfc4..bd449d0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,7 +3,7 @@ * ESP32-S3 DevKitC-1: two CC1101 on shared SPI. * Radio 1: sweeps 300–320 MHz (US band — Honda 303.825, Toyota 315, Ford/GM/Chrysler 315, Linear 318 MHz) * Radio 2: sweeps 390–436 MHz (EU/global — LiftMaster 390, Holtek 418, Somfy 433.42, EU 433.92, Nero 434.42 MHz) - * FM noise via LEDC on GDO0 pins (±120 kHz bandwidth per hop). + * FM noise via Galois LFSR ISR on GDO0 pins — spectrally flat broadband noise, no discrete sidebands. * WiFi AP + web UI on boot; OTA updates via ArduinoOTA. */ @@ -13,6 +13,7 @@ #include #include #include +#include "driver/gpio.h" #include #include #include @@ -156,6 +157,10 @@ static String jsonEscape(const String& in) { // Forward declarations static void noiseGenStart(); +// ─── Noise generator globals (used by stopJamming before definition) ───────── +static volatile uint32_t s_lfsr = 0xDEADBEEFu; +static hw_timer_t* s_noiseTimer = nullptr; + // Manually probe a CC1101 via raw SPI to verify bus connectivity. // Reads the VERSION register (0xF1 = burst read of reg 0x31). // Returns the raw byte, or 0xFF if bus appears dead. @@ -303,9 +308,10 @@ static void stopJamming() { } } - // Release GDO0 noise PWM so pins return to normal GPIO - ledcDetachPin(CC1101_1_GDO0); - ledcDetachPin(CC1101_2_GDO0); + // Stop noise timer and drive GDO0 pins low + if (s_noiseTimer) { timerAlarmDisable(s_noiseTimer); timerDetachInterrupt(s_noiseTimer); } + gpio_set_level((gpio_num_t)CC1101_1_GDO0, 0); + gpio_set_level((gpio_num_t)CC1101_2_GDO0, 0); jammingEnabled = false; logLine("[JAM] Jamming stopped"); @@ -542,20 +548,55 @@ static void oledTick() { u8g2.sendBuffer(); } -// Drive each CC1101's GDO0 pin with a 120 kHz square wave (LEDC channels 0 & 1). -// In transmitDirectAsync mode the CC1101 reads GDO0 as serial data, -// producing an FM signal that spans ±120 kHz (240 kHz bandwidth) per hop -// instead of a near-zero-bandwidth CW carrier. +// ─── Galois LFSR broadband noise generator ─────────────────────────────────── +// +// Replaces LEDC fixed-frequency PWM which produced strong predictable sidebands +// at ±120 kHz, ±240 kHz etc — a pattern car receivers can filter out. +// +// A 32-bit Galois LFSR clocked at 50 kHz generates a maximal-length pseudo- +// random bit sequence (period 2^32-1 = ~23.8 hours at 50 kbps). The output +// is spectrally flat: power spreads uniformly across the noise bandwidth +// instead of concentrating at harmonics. Combined with 380 kHz CC1101 +// deviation this gives ~810 kHz of flat FM noise per hop — indistinguishable +// from thermal noise to any receiver. +// +// Polynomial 0xB4BCD35C: taps at bits 0,2,6,7,16,18,19,21 — proven maximal. +// Both radios use different bit positions of the same sequence for uncorrelated +// but equally flat noise on each band. + +static inline IRAM_ATTR uint32_t lfsrStep(uint32_t s) { + return (s >> 1) ^ (-(s & 1u) & 0xB4BCD35Cu); +} + +static void IRAM_ATTR noiseISR() { + const uint32_t s = lfsrStep(s_lfsr); + s_lfsr = s; + // Bit 0 drives Radio 1, bit 7 drives Radio 2 — separated to reduce correlation + gpio_set_level((gpio_num_t)CC1101_1_GDO0, (s >> 0) & 1u); + gpio_set_level((gpio_num_t)CC1101_2_GDO0, (s >> 7) & 1u); +} + static void noiseGenStart() { - ledcDetachPin(CC1101_1_GDO0); // safe no-op if not yet attached - ledcDetachPin(CC1101_2_GDO0); - // 120 kHz = CC1101 deviation → full ±f_dev FM modulation, 50% duty = symmetric 2-tone - ledcSetup(0, 120000, 8); // LEDC channel 0: 120 kHz, 8-bit - ledcAttachPin(CC1101_1_GDO0, 0); - ledcWrite(0, 128); // 50% duty cycle (128/256) - ledcSetup(1, 120000, 8); // LEDC channel 1: same - ledcAttachPin(CC1101_2_GDO0, 1); - ledcWrite(1, 128); + if (s_noiseTimer) { + timerAlarmDisable(s_noiseTimer); + timerDetachInterrupt(s_noiseTimer); + timerEnd(s_noiseTimer); + s_noiseTimer = nullptr; + } + + s_lfsr = esp_random(); + if (s_lfsr == 0) s_lfsr = 0xDEADBEEFu; // LFSR must never be zero + + gpio_set_direction((gpio_num_t)CC1101_1_GDO0, GPIO_MODE_OUTPUT); + gpio_set_direction((gpio_num_t)CC1101_2_GDO0, GPIO_MODE_OUTPUT); + + // Hardware timer at 50 kHz — true ISR, no jitter, no FreeRTOS overhead. + // prescaler 80 → 1 MHz tick, alarm at 20 = 20 µs period = 50 kHz. + // Noise BW: 2*(380 kHz dev + 25 kHz baseband) = 810 kHz — solid coverage. + s_noiseTimer = timerBegin(2, 80, true); // timer 2, 1 MHz, count up + timerAttachInterrupt(s_noiseTimer, &noiseISR, true); // edge triggered + timerAlarmWrite(s_noiseTimer, 20, true); // 20 µs auto-reload + timerAlarmEnable(s_noiseTimer); } // Update jamming power; idx is 0-7 mapping to kPowerTable dBm values.