Jam modes: CW, Pulse, Special (cypher-pulse style); GDO pin fixes; flood startTransmit
- Module gpio=GDO0 for RadioLib packet timing; pinMode vs gpio matrix - SPECIAL: 60-byte blocking transmit @ 10ms like cypher-pulse - FLOOD: startTransmit; timing after burst - CW/PULSE modes; replay GDO drive before async TX - Web UI: mode buttons and telemetry jam_mode mapping Made-with: Cursor
This commit is contained in:
@@ -54,11 +54,16 @@
|
||||
#define JAM_PRECISION_DEV_R2_KHZ 55.0f
|
||||
#define JAM_PRECISION_LFSR_HZ 40000
|
||||
|
||||
// Pulse jam: slow square wave (spoofs preamble AGC)
|
||||
#define JAM_PULSE_HZ 2000
|
||||
|
||||
// Flood jam: packet-mode random payloads (mcore1976-style bursty TX); deviation for symbol spread.
|
||||
#define JAM_FLOOD_PKT_BYTES 64
|
||||
#define JAM_FLOOD_DEV_R1_KHZ 140.0f
|
||||
#define JAM_FLOOD_DEV_R2_KHZ 200.0f
|
||||
|
||||
// Special jam: 60-byte random payloads with 10ms delay (cypher-pulse exact clone).
|
||||
|
||||
// SmartRF-style dump: config space only (TI SWRS061); PATABLE/ strobes not included.
|
||||
#define CC1101_CFG_REG_LAST 0x2E
|
||||
|
||||
|
||||
188
src/main.cpp
188
src/main.cpp
@@ -24,10 +24,12 @@
|
||||
|
||||
// Shared SPI; each Module uses its own CS.
|
||||
// Must pass SPIClass explicitly so RadioLib uses our configured pins.
|
||||
// Fifth arg (gpio) must be the wired GDO0: CC1101::transmit() waits on getGpio() for packet
|
||||
// timing; RADIOLIB_NC makes digitalRead always 0 → TX timeout → flood jam silently did nothing.
|
||||
static SPIClass spi(FSPI);
|
||||
static ArduinoHal hal(spi, SPISettings(SPI_SPEED_HZ, MSBFIRST, SPI_MODE0));
|
||||
static Module mod1(&hal, CC1101_1_CS, CC1101_1_GDO0, RADIOLIB_NC, RADIOLIB_NC);
|
||||
static Module mod2(&hal, CC1101_2_CS, CC1101_2_GDO0, RADIOLIB_NC, RADIOLIB_NC);
|
||||
static Module mod1(&hal, CC1101_1_CS, CC1101_1_GDO0, RADIOLIB_NC, CC1101_1_GDO0);
|
||||
static Module mod2(&hal, CC1101_2_CS, CC1101_2_GDO0, RADIOLIB_NC, CC1101_2_GDO0);
|
||||
|
||||
CC1101 radio1(&mod1);
|
||||
CC1101 radio2(&mod2);
|
||||
@@ -46,7 +48,7 @@ static int8_t jamPower = kPowerTable[DEFAULT_JAM_POWER_IDX];
|
||||
// Policy: jam always starts after boot. NVS autoStartJam is forced true on each boot (no “boot jam off”).
|
||||
static bool autoStartJam = true;
|
||||
|
||||
enum class JamMode : uint8_t { DIRECT = 0, PRECISION = 1, FLOOD = 2 };
|
||||
enum class JamMode : uint8_t { DIRECT = 0, PRECISION = 1, FLOOD = 2, CW = 3, PULSE = 4, SPECIAL = 5 };
|
||||
static JamMode jamMode = JamMode::DIRECT;
|
||||
static bool s_floodJamActive = false;
|
||||
static uint32_t s_jamLfsrKeyHz = JAM_LFSR_KEY_HZ;
|
||||
@@ -55,12 +57,15 @@ static const char* jamModeToCstr(JamMode m) {
|
||||
switch (m) {
|
||||
case JamMode::PRECISION: return "precision";
|
||||
case JamMode::FLOOD: return "flood";
|
||||
case JamMode::CW: return "cw";
|
||||
case JamMode::PULSE: return "pulse";
|
||||
case JamMode::SPECIAL: return "special";
|
||||
default: return "direct";
|
||||
}
|
||||
}
|
||||
|
||||
static JamMode jamModeFromU8(uint8_t u) {
|
||||
if (u <= (uint8_t)JamMode::FLOOD) return (JamMode)u;
|
||||
if (u <= (uint8_t)JamMode::SPECIAL) return (JamMode)u;
|
||||
return JamMode::DIRECT;
|
||||
}
|
||||
|
||||
@@ -442,7 +447,7 @@ static void startCapture(float freq, uint8_t radioNum, bool isOOK) {
|
||||
radio.receiveDirect(); // GDO0 becomes demodulated-data output from CC1101
|
||||
|
||||
// After receiveDirect, CC1101 drives GDO0 — set ESP32 pin as input to read it
|
||||
gpio_set_direction(capGdoPin, GPIO_MODE_INPUT);
|
||||
pinMode(capGdoPin, INPUT);
|
||||
|
||||
capTimerStop();
|
||||
noInterrupts();
|
||||
@@ -492,9 +497,11 @@ static void startReplay(uint8_t radioNum) {
|
||||
spi.endTransaction();
|
||||
}
|
||||
|
||||
radio.transmitDirectAsync(); // GDO0 becomes data input to CC1101
|
||||
// Drive GDO0 output low before switching CC1101 to async TX to avoid glitching
|
||||
pinMode(capGdoPin, OUTPUT);
|
||||
digitalWrite(capGdoPin, LOW);
|
||||
|
||||
gpio_set_direction(capGdoPin, GPIO_MODE_OUTPUT);
|
||||
radio.transmitDirectAsync(); // GDO0 becomes data input to CC1101
|
||||
|
||||
capTimerStop();
|
||||
noInterrupts();
|
||||
@@ -534,8 +541,8 @@ static void stopCapture() {
|
||||
oledNotify("REPLAY", "STOPPED", 2500);
|
||||
}
|
||||
// Restore pin direction then restart jamming if it was active before capture
|
||||
gpio_set_direction(capGdoPin, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(capGdoPin, 0);
|
||||
pinMode(capGdoPin, OUTPUT);
|
||||
digitalWrite(capGdoPin, LOW);
|
||||
if (capPrevJamming) {
|
||||
capPrevJamming = false;
|
||||
jammingEnabled = true;
|
||||
@@ -772,8 +779,9 @@ static void startJamming() {
|
||||
timerEnd(s_noiseTimer);
|
||||
s_noiseTimer = nullptr;
|
||||
}
|
||||
gpio_set_level((gpio_num_t)CC1101_1_GDO0, 0);
|
||||
gpio_set_level((gpio_num_t)CC1101_2_GDO0, 0);
|
||||
// Default: inputs so begin() matches RadioLib; flood/packet TX will keep INPUT, direct jam sets OUTPUT later.
|
||||
pinMode(CC1101_1_GDO0, INPUT);
|
||||
pinMode(CC1101_2_GDO0, INPUT);
|
||||
|
||||
radio1Status = 0;
|
||||
radio2Status = 0;
|
||||
@@ -847,9 +855,17 @@ static void startJamming() {
|
||||
radio1.setFrequencyDeviation(JAM_PRECISION_DEV_R1_KHZ);
|
||||
break;
|
||||
case JamMode::FLOOD:
|
||||
case JamMode::SPECIAL:
|
||||
(void)radio1.setOOK(false);
|
||||
radio1.setFrequencyDeviation(JAM_FLOOD_DEV_R1_KHZ);
|
||||
break;
|
||||
case JamMode::CW:
|
||||
(void)radio1.setOOK(true);
|
||||
break;
|
||||
case JamMode::PULSE:
|
||||
(void)radio1.setOOK(false);
|
||||
radio1.setFrequencyDeviation(JAM_DEV_KHZ_R1_WIDE);
|
||||
break;
|
||||
case JamMode::DIRECT:
|
||||
default:
|
||||
#if JAM_R1_USE_OOK
|
||||
@@ -871,13 +887,21 @@ static void startJamming() {
|
||||
if (radio2Status == 1) {
|
||||
switch (jamMode) {
|
||||
case JamMode::PRECISION:
|
||||
(void)radio2.setOOK(false);
|
||||
radio2.setFrequencyDeviation(JAM_PRECISION_DEV_R2_KHZ);
|
||||
break;
|
||||
case JamMode::FLOOD:
|
||||
case JamMode::SPECIAL:
|
||||
(void)radio2.setOOK(false);
|
||||
radio2.setFrequencyDeviation(JAM_FLOOD_DEV_R2_KHZ);
|
||||
break;
|
||||
case JamMode::CW:
|
||||
(void)radio2.setOOK(true);
|
||||
break;
|
||||
case JamMode::PULSE:
|
||||
case JamMode::DIRECT:
|
||||
default:
|
||||
(void)radio2.setOOK(false);
|
||||
radio2.setFrequencyDeviation(JAM_DEV_KHZ_R2_WIDE);
|
||||
break;
|
||||
}
|
||||
@@ -887,18 +911,65 @@ static void startJamming() {
|
||||
int stTx1 = RADIOLIB_ERR_NONE;
|
||||
int stTx2 = RADIOLIB_ERR_NONE;
|
||||
|
||||
if (jamMode == JamMode::FLOOD) {
|
||||
logLine(String("[JAM] Mode: flood (packet PRNG, ") + String(JAM_FLOOD_PKT_BYTES) + " B) @ " +
|
||||
if (jamMode == JamMode::FLOOD || jamMode == JamMode::SPECIAL) {
|
||||
logLine(String("[JAM] Mode: ") + jamModeToCstr(jamMode) + " (packet PRNG) @ " +
|
||||
String(JAM_LOCK_FREQ_1_MHZ, 2) + " / " + String(JAM_LOCK_FREQ_2_MHZ, 2) + " MHz");
|
||||
// Packet TX: RadioLib polls GDO0 for timing — must be MCU input (not OUTPUT from LFSR path).
|
||||
pinMode(CC1101_1_GDO0, INPUT);
|
||||
pinMode(CC1101_2_GDO0, INPUT);
|
||||
if (radio1Status == 1) radio1Status = 2;
|
||||
if (radio2Status == 1) radio2Status = 2;
|
||||
s_floodJamActive = (radio1Status == 2 || radio2Status == 2);
|
||||
s_noiseEn1 = false;
|
||||
s_noiseEn2 = false;
|
||||
} else if (jamMode == JamMode::CW) {
|
||||
logLine(String("[JAM] Mode: cw (unmodulated carrier) @ ") +
|
||||
String(JAM_LOCK_FREQ_1_MHZ, 2) + " / " + String(JAM_LOCK_FREQ_2_MHZ, 2) + " MHz");
|
||||
|
||||
// Async TX with GDO0 held HIGH to generate a continuous wave in OOK mode
|
||||
pinMode(CC1101_1_GDO0, OUTPUT);
|
||||
pinMode(CC1101_2_GDO0, OUTPUT);
|
||||
digitalWrite(CC1101_1_GDO0, HIGH);
|
||||
digitalWrite(CC1101_2_GDO0, HIGH);
|
||||
|
||||
if (radio1Status == 1) {
|
||||
stTx1 = radio1.transmitDirectAsync();
|
||||
if (stTx1 != RADIOLIB_ERR_NONE) {
|
||||
radio1Status = -1;
|
||||
radio1Error = "Transmit failed: " + String(stTx1);
|
||||
logLine("[R1] transmitDirectAsync failed: " + String(stTx1));
|
||||
} else {
|
||||
radio1Status = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (radio2Status == 1) {
|
||||
stTx2 = radio2.transmitDirectAsync();
|
||||
if (stTx2 != RADIOLIB_ERR_NONE) {
|
||||
radio2Status = -1;
|
||||
radio2Error = "Transmit failed: " + String(stTx2);
|
||||
logLine("[R2] transmitDirectAsync failed: " + String(stTx2));
|
||||
} else {
|
||||
radio2Status = 2;
|
||||
}
|
||||
}
|
||||
|
||||
s_noiseEn1 = false;
|
||||
s_noiseEn2 = false;
|
||||
} else {
|
||||
s_jamLfsrKeyHz = (jamMode == JamMode::PRECISION) ? (uint32_t)JAM_PRECISION_LFSR_HZ : (uint32_t)JAM_LFSR_KEY_HZ;
|
||||
if (jamMode == JamMode::PULSE) {
|
||||
s_jamLfsrKeyHz = JAM_PULSE_HZ;
|
||||
} else {
|
||||
s_jamLfsrKeyHz = (jamMode == JamMode::PRECISION) ? (uint32_t)JAM_PRECISION_LFSR_HZ : (uint32_t)JAM_LFSR_KEY_HZ;
|
||||
}
|
||||
logLine(String("[JAM] Mode: ") + jamModeToCstr(jamMode) + " | LFSR " + String(s_jamLfsrKeyHz) + " Hz on GDO0");
|
||||
|
||||
// Async TX: CC1101 samples modulator data from GDO0 — drive the pin before CMD_TX.
|
||||
pinMode(CC1101_1_GDO0, OUTPUT);
|
||||
pinMode(CC1101_2_GDO0, OUTPUT);
|
||||
digitalWrite(CC1101_1_GDO0, LOW);
|
||||
digitalWrite(CC1101_2_GDO0, LOW);
|
||||
|
||||
if (radio1Status == 1) {
|
||||
stTx1 = radio1.transmitDirectAsync();
|
||||
if (stTx1 != RADIOLIB_ERR_NONE) {
|
||||
@@ -956,8 +1027,8 @@ static void stopJamming() {
|
||||
timerEnd(s_noiseTimer);
|
||||
s_noiseTimer = nullptr;
|
||||
}
|
||||
gpio_set_level((gpio_num_t)CC1101_1_GDO0, 0);
|
||||
gpio_set_level((gpio_num_t)CC1101_2_GDO0, 0);
|
||||
pinMode(CC1101_1_GDO0, INPUT);
|
||||
pinMode(CC1101_2_GDO0, INPUT);
|
||||
|
||||
if (radio1Status == 2) {
|
||||
int st1 = radio1.standby();
|
||||
@@ -990,18 +1061,34 @@ static void jamFloodTick() {
|
||||
if (!s_floodJamActive || !jammingEnabled) return;
|
||||
static uint8_t pkt[JAM_FLOOD_PKT_BYTES];
|
||||
static uint32_t lastMs;
|
||||
const uint32_t now = millis();
|
||||
if ((uint32_t)(now - lastMs) < 2u) return;
|
||||
lastMs = now;
|
||||
|
||||
if (jamMode == JamMode::SPECIAL) {
|
||||
// Exact cypher-pulse clone: 60-byte payload, 10ms wait, blocking transmit.
|
||||
if ((uint32_t)(millis() - lastMs) < 10u) return;
|
||||
|
||||
if (radio1Status == 2) {
|
||||
esp_fill_random(pkt, sizeof(pkt));
|
||||
(void)radio1.transmit(pkt, sizeof(pkt));
|
||||
}
|
||||
if (radio2Status == 2) {
|
||||
esp_fill_random(pkt, sizeof(pkt));
|
||||
(void)radio2.transmit(pkt, sizeof(pkt));
|
||||
if (radio1Status == 2) {
|
||||
esp_fill_random(pkt, 60);
|
||||
(void)radio1.transmit(pkt, 60);
|
||||
}
|
||||
if (radio2Status == 2) {
|
||||
esp_fill_random(pkt, 60);
|
||||
(void)radio2.transmit(pkt, 60);
|
||||
}
|
||||
} else {
|
||||
// Flood mode: 64-byte payload, 2ms wait, async transmit.
|
||||
if ((uint32_t)(millis() - lastMs) < 2u) return;
|
||||
|
||||
if (radio1Status == 2) {
|
||||
esp_fill_random(pkt, sizeof(pkt));
|
||||
(void)radio1.startTransmit(pkt, sizeof(pkt));
|
||||
}
|
||||
if (radio2Status == 2) {
|
||||
esp_fill_random(pkt, sizeof(pkt));
|
||||
(void)radio2.startTransmit(pkt, sizeof(pkt));
|
||||
}
|
||||
}
|
||||
|
||||
lastMs = millis(); // record time AFTER transmit completes
|
||||
}
|
||||
|
||||
static int hexNibble(char c) {
|
||||
@@ -1022,6 +1109,12 @@ static void handleJamMode() {
|
||||
newM = JamMode::FLOOD;
|
||||
} else if (body.indexOf("\"mode\":\"precision\"") >= 0) {
|
||||
newM = JamMode::PRECISION;
|
||||
} else if (body.indexOf("\"mode\":\"cw\"") >= 0) {
|
||||
newM = JamMode::CW;
|
||||
} else if (body.indexOf("\"mode\":\"pulse\"") >= 0) {
|
||||
newM = JamMode::PULSE;
|
||||
} else if (body.indexOf("\"mode\":\"special\"") >= 0) {
|
||||
newM = JamMode::SPECIAL;
|
||||
} else if (body.indexOf("\"mode\":\"direct\"") >= 0) {
|
||||
newM = JamMode::DIRECT;
|
||||
} else {
|
||||
@@ -1464,12 +1557,21 @@ static inline IRAM_ATTR uint32_t lfsrStep(uint32_t s) {
|
||||
}
|
||||
|
||||
static void IRAM_ATTR noiseISR() {
|
||||
const uint32_t s = lfsrStep(s_lfsr);
|
||||
s_lfsr = s;
|
||||
if (s_noiseEn1) gpio_set_level((gpio_num_t)CC1101_1_GDO0, (s >> 0) & 1u);
|
||||
else gpio_set_level((gpio_num_t)CC1101_1_GDO0, 0);
|
||||
if (s_noiseEn2) gpio_set_level((gpio_num_t)CC1101_2_GDO0, (s >> 7) & 1u);
|
||||
else gpio_set_level((gpio_num_t)CC1101_2_GDO0, 0);
|
||||
if (jamMode == JamMode::PULSE) {
|
||||
static uint32_t pulseToggle = 0;
|
||||
pulseToggle ^= 1;
|
||||
if (s_noiseEn1) gpio_set_level((gpio_num_t)CC1101_1_GDO0, pulseToggle);
|
||||
else gpio_set_level((gpio_num_t)CC1101_1_GDO0, 0);
|
||||
if (s_noiseEn2) gpio_set_level((gpio_num_t)CC1101_2_GDO0, pulseToggle);
|
||||
else gpio_set_level((gpio_num_t)CC1101_2_GDO0, 0);
|
||||
} else {
|
||||
const uint32_t s = lfsrStep(s_lfsr);
|
||||
s_lfsr = s;
|
||||
if (s_noiseEn1) gpio_set_level((gpio_num_t)CC1101_1_GDO0, (s >> 0) & 1u);
|
||||
else gpio_set_level((gpio_num_t)CC1101_1_GDO0, 0);
|
||||
if (s_noiseEn2) gpio_set_level((gpio_num_t)CC1101_2_GDO0, (s >> 7) & 1u);
|
||||
else gpio_set_level((gpio_num_t)CC1101_2_GDO0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void noiseGenStart() {
|
||||
@@ -1483,8 +1585,8 @@ static void noiseGenStart() {
|
||||
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);
|
||||
pinMode(CC1101_1_GDO0, OUTPUT);
|
||||
pinMode(CC1101_2_GDO0, OUTPUT);
|
||||
|
||||
// Hardware timer at JAM_LFSR_KEY_HZ — true ISR, no jitter, no FreeRTOS overhead.
|
||||
// prescaler 80 → 1 MHz tick; alarm period = 1e6 / JAM_LFSR_KEY_HZ µs.
|
||||
@@ -1682,11 +1784,14 @@ textarea:focus{border-color:#4fbf59;outline:none}
|
||||
<h2>Controls</h2>
|
||||
<p style="font-size:10px;color:#3a7a3e;margin-bottom:8px">TX power is fixed at +10 dBm (CC1101 max, TI SWRS061). Radiated level includes your antenna gain only — no external PA.</p>
|
||||
<p style="font-size:10px;color:#4fbf59;margin-bottom:8px">Jamming starts automatically on every power-up. Press <strong>Stop Jamming</strong> to go standby for capture (REC), then start jam again when done.</p>
|
||||
<p style="font-size:10px;color:#3a7a3e;margin-bottom:6px"><strong>Jam strategy</strong> — Direct: wide LFSR + async TX. Precision: narrower deviation / key rate. Flood: bursty random packets (hypothesis for burst-sensitive links; verify in your chamber).</p>
|
||||
<p style="font-size:10px;color:#3a7a3e;margin-bottom:6px"><strong>Jam strategy</strong> — Direct: wide LFSR + async TX. Precision: narrower deviation / key rate. Flood: bursty random packets (hypothesis for burst-sensitive links; verify in your chamber). CW: unmodulated carrier. Pulse: slow square wave (spoofs preamble AGC). Special: 60-byte blocking packet TX with 10ms delay (cypher-pulse clone).</p>
|
||||
<div style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:10px">
|
||||
<button type="button" class="jm" id="jmDirect">Direct</button>
|
||||
<button type="button" class="jm" id="jmPrecision">Precision</button>
|
||||
<button type="button" class="jm" id="jmFlood">Flood</button>
|
||||
<button type="button" class="jm" id="jmCw">CW</button>
|
||||
<button type="button" class="jm" id="jmPulse">Pulse</button>
|
||||
<button type="button" class="jm" id="jmSpecial">Special</button>
|
||||
</div>
|
||||
<div class="row" style="margin-bottom:10px">
|
||||
<div class="col" style="display:flex;flex-direction:column;gap:6px">
|
||||
@@ -1901,7 +2006,7 @@ function applyTelemetry(t){
|
||||
if(!ok2)parts.push('R2 FAULT now');
|
||||
if(jam&&(tx1||tx2)){
|
||||
const b=[];if(tx1)b.push('315 TX');if(tx2)b.push('433.92 TX');
|
||||
const jm=t.jam_mode_str||({0:'direct',1:'precision',2:'flood'}[t.jam_mode])||'direct';
|
||||
const jm=t.jam_mode_str||({0:'direct',1:'precision',2:'flood',3:'cw',4:'pulse',5:'special'}[t.jam_mode])||'direct';
|
||||
parts.push(jm+' | '+b.join(' + '));
|
||||
}
|
||||
document.getElementById('bs').textContent=parts.length?parts.join(' | ')+' | '+t.jam_power+' dBm chip (ERP + antenna only)'
|
||||
@@ -1958,8 +2063,8 @@ function applyTelemetry(t){
|
||||
drawSp('cT',hT,'#86f28a',20,90);
|
||||
drawSp('cH',hH,'#4fbf59',0,320);
|
||||
const cd=document.getElementById('connDot');cd.style.background='#86f28a';cd.style.boxShadow='0 0 8px rgba(134,242,138,.7)';
|
||||
const jmStr=(t.jam_mode_str||({0:'direct',1:'precision',2:'flood'}[t.jam_mode])||'direct').toLowerCase();
|
||||
[['jmDirect','direct'],['jmPrecision','precision'],['jmFlood','flood']].forEach(([id,exp])=>{
|
||||
const jmStr=(t.jam_mode_str||({0:'direct',1:'precision',2:'flood',3:'cw',4:'pulse',5:'special'}[t.jam_mode])||'direct').toLowerCase();
|
||||
[['jmDirect','direct'],['jmPrecision','precision'],['jmFlood','flood'],['jmCw','cw'],['jmPulse','pulse'],['jmSpecial','special']].forEach(([id,exp])=>{
|
||||
const el=document.getElementById(id);if(!el)return;
|
||||
el.classList.toggle('jm-active',jmStr===exp);
|
||||
});
|
||||
@@ -2018,6 +2123,9 @@ document.getElementById('dl').addEventListener('click',()=>{const a=document.cre
|
||||
document.getElementById('jmDirect').addEventListener('click',()=>setJamMode('direct'));
|
||||
document.getElementById('jmPrecision').addEventListener('click',()=>setJamMode('precision'));
|
||||
document.getElementById('jmFlood').addEventListener('click',()=>setJamMode('flood'));
|
||||
document.getElementById('jmCw').addEventListener('click',()=>setJamMode('cw'));
|
||||
document.getElementById('jmPulse').addEventListener('click',()=>setJamMode('pulse'));
|
||||
document.getElementById('jmSpecial').addEventListener('click',()=>setJamMode('special'));
|
||||
|
||||
async function regExport(radio){
|
||||
try{
|
||||
@@ -2663,8 +2771,8 @@ void loop() {
|
||||
capBufFull = false;
|
||||
capMode = CapMode::RECORDED;
|
||||
interrupts();
|
||||
gpio_set_direction(capGdoPin, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(capGdoPin, 0);
|
||||
pinMode(capGdoPin, OUTPUT);
|
||||
digitalWrite(capGdoPin, LOW);
|
||||
logLine("[CAP] Buffer full: " + String(capRecBits) + " bits (" +
|
||||
String(capRecBits * 1000 / CAP_SAMPLE_HZ) + " ms) captured");
|
||||
oledNotify("CAPTURED", (String(capRecBits * 1000 / CAP_SAMPLE_HZ) + "ms").c_str());
|
||||
|
||||
Reference in New Issue
Block a user